46242deee8d21cf28b5ef4bba516292b1bb9febe
[linux-3.10.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 #include "../../arch/common.h"
15
16 #include "../browser.h"
17 #include "../helpline.h"
18 #include "../util.h"
19 #include "../ui.h"
20 #include "map.h"
21
22 struct hist_browser {
23         struct ui_browser   b;
24         struct hists        *hists;
25         struct hist_entry   *he_selection;
26         struct map_symbol   *selection;
27         int                  print_seq;
28         bool                 show_dso;
29         bool                 has_symbols;
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 int hist_browser__run(struct hist_browser *browser, const char *ev_name,
314                              struct hist_browser_timer *hbt)
315 {
316         int key;
317         char title[160];
318         int delay_secs = hbt ? hbt->refresh : 0;
319
320         browser->b.entries = &browser->hists->entries;
321         browser->b.nr_entries = browser->hists->nr_entries;
322
323         hist_browser__refresh_dimensions(browser);
324         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
325
326         if (ui_browser__show(&browser->b, title,
327                              "Press '?' for help on key bindings") < 0)
328                 return -1;
329
330         while (1) {
331                 key = ui_browser__run(&browser->b, delay_secs);
332
333                 switch (key) {
334                 case K_TIMER:
335                         hbt->timer(hbt->arg);
336                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
337
338                         if (browser->hists->stats.nr_lost_warned !=
339                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
340                                 browser->hists->stats.nr_lost_warned =
341                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
342                                 ui_browser__warn_lost_events(&browser->b);
343                         }
344
345                         hists__browser_title(browser->hists, title, sizeof(title), ev_name);
346                         ui_browser__show_title(&browser->b, title);
347                         continue;
348                 case 'D': { /* Debug */
349                         static int seq;
350                         struct hist_entry *h = rb_entry(browser->b.top,
351                                                         struct hist_entry, rb_node);
352                         ui_helpline__pop();
353                         ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
354                                            seq++, browser->b.nr_entries,
355                                            browser->hists->nr_entries,
356                                            browser->b.height,
357                                            browser->b.index,
358                                            browser->b.top_idx,
359                                            h->row_offset, h->nr_rows);
360                 }
361                         break;
362                 case 'C':
363                         /* Collapse the whole world. */
364                         hist_browser__set_folding(browser, false);
365                         break;
366                 case 'E':
367                         /* Expand the whole world. */
368                         hist_browser__set_folding(browser, true);
369                         break;
370                 case K_ENTER:
371                         if (hist_browser__toggle_fold(browser))
372                                 break;
373                         /* fall thru */
374                 default:
375                         goto out;
376                 }
377         }
378 out:
379         ui_browser__hide(&browser->b);
380         return key;
381 }
382
383 static char *callchain_list__sym_name(struct callchain_list *cl,
384                                       char *bf, size_t bfsize, bool show_dso)
385 {
386         int printed;
387
388         if (cl->ms.sym)
389                 printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
390         else
391                 printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
392
393         if (show_dso)
394                 scnprintf(bf + printed, bfsize - printed, " %s",
395                           cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
396
397         return bf;
398 }
399
400 #define LEVEL_OFFSET_STEP 3
401
402 static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
403                                                      struct callchain_node *chain_node,
404                                                      u64 total, int level,
405                                                      unsigned short row,
406                                                      off_t *row_offset,
407                                                      bool *is_current_entry)
408 {
409         struct rb_node *node;
410         int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
411         u64 new_total, remaining;
412
413         if (callchain_param.mode == CHAIN_GRAPH_REL)
414                 new_total = chain_node->children_hit;
415         else
416                 new_total = total;
417
418         remaining = new_total;
419         node = rb_first(&chain_node->rb_root);
420         while (node) {
421                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
422                 struct rb_node *next = rb_next(node);
423                 u64 cumul = callchain_cumul_hits(child);
424                 struct callchain_list *chain;
425                 char folded_sign = ' ';
426                 int first = true;
427                 int extra_offset = 0;
428
429                 remaining -= cumul;
430
431                 list_for_each_entry(chain, &child->val, list) {
432                         char bf[1024], *alloc_str;
433                         const char *str;
434                         int color;
435                         bool was_first = first;
436
437                         if (first)
438                                 first = false;
439                         else
440                                 extra_offset = LEVEL_OFFSET_STEP;
441
442                         folded_sign = callchain_list__folded(chain);
443                         if (*row_offset != 0) {
444                                 --*row_offset;
445                                 goto do_next;
446                         }
447
448                         alloc_str = NULL;
449                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
450                                                        browser->show_dso);
451                         if (was_first) {
452                                 double percent = cumul * 100.0 / new_total;
453
454                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
455                                         str = "Not enough memory!";
456                                 else
457                                         str = alloc_str;
458                         }
459
460                         color = HE_COLORSET_NORMAL;
461                         width = browser->b.width - (offset + extra_offset + 2);
462                         if (ui_browser__is_current_entry(&browser->b, row)) {
463                                 browser->selection = &chain->ms;
464                                 color = HE_COLORSET_SELECTED;
465                                 *is_current_entry = true;
466                         }
467
468                         ui_browser__set_color(&browser->b, color);
469                         ui_browser__gotorc(&browser->b, row, 0);
470                         slsmg_write_nstring(" ", offset + extra_offset);
471                         slsmg_printf("%c ", folded_sign);
472                         slsmg_write_nstring(str, width);
473                         free(alloc_str);
474
475                         if (++row == browser->b.height)
476                                 goto out;
477 do_next:
478                         if (folded_sign == '+')
479                                 break;
480                 }
481
482                 if (folded_sign == '-') {
483                         const int new_level = level + (extra_offset ? 2 : 1);
484                         row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
485                                                                          new_level, row, row_offset,
486                                                                          is_current_entry);
487                 }
488                 if (row == browser->b.height)
489                         goto out;
490                 node = next;
491         }
492 out:
493         return row - first_row;
494 }
495
496 static int hist_browser__show_callchain_node(struct hist_browser *browser,
497                                              struct callchain_node *node,
498                                              int level, unsigned short row,
499                                              off_t *row_offset,
500                                              bool *is_current_entry)
501 {
502         struct callchain_list *chain;
503         int first_row = row,
504              offset = level * LEVEL_OFFSET_STEP,
505              width = browser->b.width - offset;
506         char folded_sign = ' ';
507
508         list_for_each_entry(chain, &node->val, list) {
509                 char bf[1024], *s;
510                 int color;
511
512                 folded_sign = callchain_list__folded(chain);
513
514                 if (*row_offset != 0) {
515                         --*row_offset;
516                         continue;
517                 }
518
519                 color = HE_COLORSET_NORMAL;
520                 if (ui_browser__is_current_entry(&browser->b, row)) {
521                         browser->selection = &chain->ms;
522                         color = HE_COLORSET_SELECTED;
523                         *is_current_entry = true;
524                 }
525
526                 s = callchain_list__sym_name(chain, bf, sizeof(bf),
527                                              browser->show_dso);
528                 ui_browser__gotorc(&browser->b, row, 0);
529                 ui_browser__set_color(&browser->b, color);
530                 slsmg_write_nstring(" ", offset);
531                 slsmg_printf("%c ", folded_sign);
532                 slsmg_write_nstring(s, width - 2);
533
534                 if (++row == browser->b.height)
535                         goto out;
536         }
537
538         if (folded_sign == '-')
539                 row += hist_browser__show_callchain_node_rb_tree(browser, node,
540                                                                  browser->hists->stats.total_period,
541                                                                  level + 1, row,
542                                                                  row_offset,
543                                                                  is_current_entry);
544 out:
545         return row - first_row;
546 }
547
548 static int hist_browser__show_callchain(struct hist_browser *browser,
549                                         struct rb_root *chain,
550                                         int level, unsigned short row,
551                                         off_t *row_offset,
552                                         bool *is_current_entry)
553 {
554         struct rb_node *nd;
555         int first_row = row;
556
557         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
558                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
559
560                 row += hist_browser__show_callchain_node(browser, node, level,
561                                                          row, row_offset,
562                                                          is_current_entry);
563                 if (row == browser->b.height)
564                         break;
565         }
566
567         return row - first_row;
568 }
569
570 static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
571                             u64 (*get_field)(struct hist_entry *))
572 {
573         int ret;
574         double percent = 0.0;
575         struct hists *hists = he->hists;
576
577         if (hists->stats.total_period)
578                 percent = 100.0 * get_field(he) / hists->stats.total_period;
579
580         *(double *)hpp->ptr = percent;
581
582         ret = scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
583         return ret;
584 }
585
586 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
587 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
588 {                                                                       \
589         return he->stat._field;                                         \
590 }                                                                       \
591                                                                         \
592 static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp,        \
593                                            struct hist_entry *he)       \
594 {                                                                       \
595         return __hpp__color_fmt(hpp, he, __hpp_get_##_field);           \
596 }
597
598 __HPP_COLOR_PERCENT_FN(overhead, period)
599 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
600 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
601 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
602 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
603
604 #undef __HPP_COLOR_PERCENT_FN
605
606 void hist_browser__init_hpp(void)
607 {
608         perf_hpp__column_enable(PERF_HPP__OVERHEAD);
609
610         perf_hpp__init();
611
612         perf_hpp__format[PERF_HPP__OVERHEAD].color =
613                                 hist_browser__hpp_color_overhead;
614         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
615                                 hist_browser__hpp_color_overhead_sys;
616         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
617                                 hist_browser__hpp_color_overhead_us;
618         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
619                                 hist_browser__hpp_color_overhead_guest_sys;
620         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
621                                 hist_browser__hpp_color_overhead_guest_us;
622 }
623
624 static int hist_browser__show_entry(struct hist_browser *browser,
625                                     struct hist_entry *entry,
626                                     unsigned short row)
627 {
628         char s[256];
629         double percent;
630         int printed = 0;
631         int width = browser->b.width;
632         char folded_sign = ' ';
633         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
634         off_t row_offset = entry->row_offset;
635         bool first = true;
636         struct perf_hpp_fmt *fmt;
637
638         if (current_entry) {
639                 browser->he_selection = entry;
640                 browser->selection = &entry->ms;
641         }
642
643         if (symbol_conf.use_callchain) {
644                 hist_entry__init_have_children(entry);
645                 folded_sign = hist_entry__folded(entry);
646         }
647
648         if (row_offset == 0) {
649                 struct perf_hpp hpp = {
650                         .buf            = s,
651                         .size           = sizeof(s),
652                 };
653                 int i = 0;
654
655                 ui_browser__gotorc(&browser->b, row, 0);
656
657                 perf_hpp__for_each_format(fmt) {
658
659                         if (!first) {
660                                 slsmg_printf("  ");
661                                 width -= 2;
662                         }
663                         first = false;
664
665                         if (fmt->color) {
666                                 hpp.ptr = &percent;
667                                 /* It will set percent for us. See __hpp__color_fmt above. */
668                                 width -= fmt->color(&hpp, entry);
669
670                                 ui_browser__set_percent_color(&browser->b, percent, current_entry);
671
672                                 if (!i && symbol_conf.use_callchain) {
673                                         slsmg_printf("%c ", folded_sign);
674                                         width -= 2;
675                                 }
676
677                                 slsmg_printf("%s", s);
678
679                                 if (!current_entry || !browser->b.navkeypressed)
680                                         ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
681                         } else {
682                                 width -= fmt->entry(&hpp, entry);
683                                 slsmg_printf("%s", s);
684                         }
685
686                         i++;
687                 }
688
689                 /* The scroll bar isn't being used */
690                 if (!browser->b.navkeypressed)
691                         width += 1;
692
693                 hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
694                 slsmg_write_nstring(s, width);
695                 ++row;
696                 ++printed;
697         } else
698                 --row_offset;
699
700         if (folded_sign == '-' && row != browser->b.height) {
701                 printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
702                                                         1, row, &row_offset,
703                                                         &current_entry);
704                 if (current_entry)
705                         browser->he_selection = entry;
706         }
707
708         return printed;
709 }
710
711 static void ui_browser__hists_init_top(struct ui_browser *browser)
712 {
713         if (browser->top == NULL) {
714                 struct hist_browser *hb;
715
716                 hb = container_of(browser, struct hist_browser, b);
717                 browser->top = rb_first(&hb->hists->entries);
718         }
719 }
720
721 static unsigned int hist_browser__refresh(struct ui_browser *browser)
722 {
723         unsigned row = 0;
724         struct rb_node *nd;
725         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
726
727         ui_browser__hists_init_top(browser);
728
729         for (nd = browser->top; nd; nd = rb_next(nd)) {
730                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
731
732                 if (h->filtered)
733                         continue;
734
735                 row += hist_browser__show_entry(hb, h, row);
736                 if (row == browser->height)
737                         break;
738         }
739
740         return row;
741 }
742
743 static struct rb_node *hists__filter_entries(struct rb_node *nd)
744 {
745         while (nd != NULL) {
746                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
747                 if (!h->filtered)
748                         return nd;
749
750                 nd = rb_next(nd);
751         }
752
753         return NULL;
754 }
755
756 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
757 {
758         while (nd != NULL) {
759                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
760                 if (!h->filtered)
761                         return nd;
762
763                 nd = rb_prev(nd);
764         }
765
766         return NULL;
767 }
768
769 static void ui_browser__hists_seek(struct ui_browser *browser,
770                                    off_t offset, int whence)
771 {
772         struct hist_entry *h;
773         struct rb_node *nd;
774         bool first = true;
775
776         if (browser->nr_entries == 0)
777                 return;
778
779         ui_browser__hists_init_top(browser);
780
781         switch (whence) {
782         case SEEK_SET:
783                 nd = hists__filter_entries(rb_first(browser->entries));
784                 break;
785         case SEEK_CUR:
786                 nd = browser->top;
787                 goto do_offset;
788         case SEEK_END:
789                 nd = hists__filter_prev_entries(rb_last(browser->entries));
790                 first = false;
791                 break;
792         default:
793                 return;
794         }
795
796         /*
797          * Moves not relative to the first visible entry invalidates its
798          * row_offset:
799          */
800         h = rb_entry(browser->top, struct hist_entry, rb_node);
801         h->row_offset = 0;
802
803         /*
804          * Here we have to check if nd is expanded (+), if it is we can't go
805          * the next top level hist_entry, instead we must compute an offset of
806          * what _not_ to show and not change the first visible entry.
807          *
808          * This offset increments when we are going from top to bottom and
809          * decreases when we're going from bottom to top.
810          *
811          * As we don't have backpointers to the top level in the callchains
812          * structure, we need to always print the whole hist_entry callchain,
813          * skipping the first ones that are before the first visible entry
814          * and stop when we printed enough lines to fill the screen.
815          */
816 do_offset:
817         if (offset > 0) {
818                 do {
819                         h = rb_entry(nd, struct hist_entry, rb_node);
820                         if (h->ms.unfolded) {
821                                 u16 remaining = h->nr_rows - h->row_offset;
822                                 if (offset > remaining) {
823                                         offset -= remaining;
824                                         h->row_offset = 0;
825                                 } else {
826                                         h->row_offset += offset;
827                                         offset = 0;
828                                         browser->top = nd;
829                                         break;
830                                 }
831                         }
832                         nd = hists__filter_entries(rb_next(nd));
833                         if (nd == NULL)
834                                 break;
835                         --offset;
836                         browser->top = nd;
837                 } while (offset != 0);
838         } else if (offset < 0) {
839                 while (1) {
840                         h = rb_entry(nd, struct hist_entry, rb_node);
841                         if (h->ms.unfolded) {
842                                 if (first) {
843                                         if (-offset > h->row_offset) {
844                                                 offset += h->row_offset;
845                                                 h->row_offset = 0;
846                                         } else {
847                                                 h->row_offset += offset;
848                                                 offset = 0;
849                                                 browser->top = nd;
850                                                 break;
851                                         }
852                                 } else {
853                                         if (-offset > h->nr_rows) {
854                                                 offset += h->nr_rows;
855                                                 h->row_offset = 0;
856                                         } else {
857                                                 h->row_offset = h->nr_rows + offset;
858                                                 offset = 0;
859                                                 browser->top = nd;
860                                                 break;
861                                         }
862                                 }
863                         }
864
865                         nd = hists__filter_prev_entries(rb_prev(nd));
866                         if (nd == NULL)
867                                 break;
868                         ++offset;
869                         browser->top = nd;
870                         if (offset == 0) {
871                                 /*
872                                  * Last unfiltered hist_entry, check if it is
873                                  * unfolded, if it is then we should have
874                                  * row_offset at its last entry.
875                                  */
876                                 h = rb_entry(nd, struct hist_entry, rb_node);
877                                 if (h->ms.unfolded)
878                                         h->row_offset = h->nr_rows;
879                                 break;
880                         }
881                         first = false;
882                 }
883         } else {
884                 browser->top = nd;
885                 h = rb_entry(nd, struct hist_entry, rb_node);
886                 h->row_offset = 0;
887         }
888 }
889
890 static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
891                                                         struct callchain_node *chain_node,
892                                                         u64 total, int level,
893                                                         FILE *fp)
894 {
895         struct rb_node *node;
896         int offset = level * LEVEL_OFFSET_STEP;
897         u64 new_total, remaining;
898         int printed = 0;
899
900         if (callchain_param.mode == CHAIN_GRAPH_REL)
901                 new_total = chain_node->children_hit;
902         else
903                 new_total = total;
904
905         remaining = new_total;
906         node = rb_first(&chain_node->rb_root);
907         while (node) {
908                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
909                 struct rb_node *next = rb_next(node);
910                 u64 cumul = callchain_cumul_hits(child);
911                 struct callchain_list *chain;
912                 char folded_sign = ' ';
913                 int first = true;
914                 int extra_offset = 0;
915
916                 remaining -= cumul;
917
918                 list_for_each_entry(chain, &child->val, list) {
919                         char bf[1024], *alloc_str;
920                         const char *str;
921                         bool was_first = first;
922
923                         if (first)
924                                 first = false;
925                         else
926                                 extra_offset = LEVEL_OFFSET_STEP;
927
928                         folded_sign = callchain_list__folded(chain);
929
930                         alloc_str = NULL;
931                         str = callchain_list__sym_name(chain, bf, sizeof(bf),
932                                                        browser->show_dso);
933                         if (was_first) {
934                                 double percent = cumul * 100.0 / new_total;
935
936                                 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
937                                         str = "Not enough memory!";
938                                 else
939                                         str = alloc_str;
940                         }
941
942                         printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
943                         free(alloc_str);
944                         if (folded_sign == '+')
945                                 break;
946                 }
947
948                 if (folded_sign == '-') {
949                         const int new_level = level + (extra_offset ? 2 : 1);
950                         printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
951                                                                                 new_level, fp);
952                 }
953
954                 node = next;
955         }
956
957         return printed;
958 }
959
960 static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
961                                                 struct callchain_node *node,
962                                                 int level, FILE *fp)
963 {
964         struct callchain_list *chain;
965         int offset = level * LEVEL_OFFSET_STEP;
966         char folded_sign = ' ';
967         int printed = 0;
968
969         list_for_each_entry(chain, &node->val, list) {
970                 char bf[1024], *s;
971
972                 folded_sign = callchain_list__folded(chain);
973                 s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
974                 printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
975         }
976
977         if (folded_sign == '-')
978                 printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
979                                                                         browser->hists->stats.total_period,
980                                                                         level + 1,  fp);
981         return printed;
982 }
983
984 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
985                                            struct rb_root *chain, int level, FILE *fp)
986 {
987         struct rb_node *nd;
988         int printed = 0;
989
990         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
991                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
992
993                 printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
994         }
995
996         return printed;
997 }
998
999 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1000                                        struct hist_entry *he, FILE *fp)
1001 {
1002         char s[8192];
1003         double percent;
1004         int printed = 0;
1005         char folded_sign = ' ';
1006
1007         if (symbol_conf.use_callchain)
1008                 folded_sign = hist_entry__folded(he);
1009
1010         hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
1011         percent = (he->stat.period * 100.0) / browser->hists->stats.total_period;
1012
1013         if (symbol_conf.use_callchain)
1014                 printed += fprintf(fp, "%c ", folded_sign);
1015
1016         printed += fprintf(fp, " %5.2f%%", percent);
1017
1018         if (symbol_conf.show_nr_samples)
1019                 printed += fprintf(fp, " %11u", he->stat.nr_events);
1020
1021         if (symbol_conf.show_total_period)
1022                 printed += fprintf(fp, " %12" PRIu64, he->stat.period);
1023
1024         printed += fprintf(fp, "%s\n", rtrim(s));
1025
1026         if (folded_sign == '-')
1027                 printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
1028
1029         return printed;
1030 }
1031
1032 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1033 {
1034         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries));
1035         int printed = 0;
1036
1037         while (nd) {
1038                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1039
1040                 printed += hist_browser__fprintf_entry(browser, h, fp);
1041                 nd = hists__filter_entries(rb_next(nd));
1042         }
1043
1044         return printed;
1045 }
1046
1047 static int hist_browser__dump(struct hist_browser *browser)
1048 {
1049         char filename[64];
1050         FILE *fp;
1051
1052         while (1) {
1053                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1054                 if (access(filename, F_OK))
1055                         break;
1056                 /*
1057                  * XXX: Just an arbitrary lazy upper limit
1058                  */
1059                 if (++browser->print_seq == 8192) {
1060                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1061                         return -1;
1062                 }
1063         }
1064
1065         fp = fopen(filename, "w");
1066         if (fp == NULL) {
1067                 char bf[64];
1068                 const char *err = strerror_r(errno, bf, sizeof(bf));
1069                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1070                 return -1;
1071         }
1072
1073         ++browser->print_seq;
1074         hist_browser__fprintf(browser, fp);
1075         fclose(fp);
1076         ui_helpline__fpush("%s written!", filename);
1077
1078         return 0;
1079 }
1080
1081 static struct hist_browser *hist_browser__new(struct hists *hists)
1082 {
1083         struct hist_browser *browser = zalloc(sizeof(*browser));
1084
1085         if (browser) {
1086                 browser->hists = hists;
1087                 browser->b.refresh = hist_browser__refresh;
1088                 browser->b.seek = ui_browser__hists_seek;
1089                 browser->b.use_navkeypressed = true;
1090                 if (sort__branch_mode == 1)
1091                         browser->has_symbols = sort_sym_from.list.next != NULL;
1092                 else
1093                         browser->has_symbols = sort_sym.list.next != NULL;
1094         }
1095
1096         return browser;
1097 }
1098
1099 static void hist_browser__delete(struct hist_browser *browser)
1100 {
1101         free(browser);
1102 }
1103
1104 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1105 {
1106         return browser->he_selection;
1107 }
1108
1109 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1110 {
1111         return browser->he_selection->thread;
1112 }
1113
1114 static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1115                                 const char *ev_name)
1116 {
1117         char unit;
1118         int printed;
1119         const struct dso *dso = hists->dso_filter;
1120         const struct thread *thread = hists->thread_filter;
1121         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1122         u64 nr_events = hists->stats.total_period;
1123
1124         nr_samples = convert_unit(nr_samples, &unit);
1125         printed = scnprintf(bf, size,
1126                            "Samples: %lu%c of event '%s', Event count (approx.): %lu",
1127                            nr_samples, unit, ev_name, nr_events);
1128
1129
1130         if (hists->uid_filter_str)
1131                 printed += snprintf(bf + printed, size - printed,
1132                                     ", UID: %s", hists->uid_filter_str);
1133         if (thread)
1134                 printed += scnprintf(bf + printed, size - printed,
1135                                     ", Thread: %s(%d)",
1136                                     (thread->comm_set ? thread->comm : ""),
1137                                     thread->pid);
1138         if (dso)
1139                 printed += scnprintf(bf + printed, size - printed,
1140                                     ", DSO: %s", dso->short_name);
1141         return printed;
1142 }
1143
1144 static inline void free_popup_options(char **options, int n)
1145 {
1146         int i;
1147
1148         for (i = 0; i < n; ++i) {
1149                 free(options[i]);
1150                 options[i] = NULL;
1151         }
1152 }
1153
1154 /* Check whether the browser is for 'top' or 'report' */
1155 static inline bool is_report_browser(void *timer)
1156 {
1157         return timer == NULL;
1158 }
1159
1160 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1161                                     const char *helpline, const char *ev_name,
1162                                     bool left_exits,
1163                                     struct hist_browser_timer *hbt,
1164                                     struct perf_session_env *env)
1165 {
1166         struct hists *hists = &evsel->hists;
1167         struct hist_browser *browser = hist_browser__new(hists);
1168         struct branch_info *bi;
1169         struct pstack *fstack;
1170         char *options[16];
1171         int nr_options = 0;
1172         int key = -1;
1173         char buf[64];
1174         char script_opt[64];
1175         int delay_secs = hbt ? hbt->refresh : 0;
1176
1177         if (browser == NULL)
1178                 return -1;
1179
1180         fstack = pstack__new(2);
1181         if (fstack == NULL)
1182                 goto out;
1183
1184         ui_helpline__push(helpline);
1185
1186         memset(options, 0, sizeof(options));
1187
1188         while (1) {
1189                 const struct thread *thread = NULL;
1190                 const struct dso *dso = NULL;
1191                 int choice = 0,
1192                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1193                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1194                 int scripts_comm = -2, scripts_symbol = -2, scripts_all = -2;
1195
1196                 nr_options = 0;
1197
1198                 key = hist_browser__run(browser, ev_name, hbt);
1199
1200                 if (browser->he_selection != NULL) {
1201                         thread = hist_browser__selected_thread(browser);
1202                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1203                 }
1204                 switch (key) {
1205                 case K_TAB:
1206                 case K_UNTAB:
1207                         if (nr_events == 1)
1208                                 continue;
1209                         /*
1210                          * Exit the browser, let hists__browser_tree
1211                          * go to the next or previous
1212                          */
1213                         goto out_free_stack;
1214                 case 'a':
1215                         if (!browser->has_symbols) {
1216                                 ui_browser__warning(&browser->b, delay_secs * 2,
1217                         "Annotation is only available for symbolic views, "
1218                         "include \"sym*\" in --sort to use it.");
1219                                 continue;
1220                         }
1221
1222                         if (browser->selection == NULL ||
1223                             browser->selection->sym == NULL ||
1224                             browser->selection->map->dso->annotate_warned)
1225                                 continue;
1226                         goto do_annotate;
1227                 case 'P':
1228                         hist_browser__dump(browser);
1229                         continue;
1230                 case 'd':
1231                         goto zoom_dso;
1232                 case 'V':
1233                         browser->show_dso = !browser->show_dso;
1234                         continue;
1235                 case 't':
1236                         goto zoom_thread;
1237                 case '/':
1238                         if (ui_browser__input_window("Symbol to show",
1239                                         "Please enter the name of symbol you want to see",
1240                                         buf, "ENTER: OK, ESC: Cancel",
1241                                         delay_secs * 2) == K_ENTER) {
1242                                 hists->symbol_filter_str = *buf ? buf : NULL;
1243                                 hists__filter_by_symbol(hists);
1244                                 hist_browser__reset(browser);
1245                         }
1246                         continue;
1247                 case 'r':
1248                         if (is_report_browser(hbt))
1249                                 goto do_scripts;
1250                         continue;
1251                 case K_F1:
1252                 case 'h':
1253                 case '?':
1254                         ui_browser__help_window(&browser->b,
1255                                         "h/?/F1        Show this window\n"
1256                                         "UP/DOWN/PGUP\n"
1257                                         "PGDN/SPACE    Navigate\n"
1258                                         "q/ESC/CTRL+C  Exit browser\n\n"
1259                                         "For multiple event sessions:\n\n"
1260                                         "TAB/UNTAB Switch events\n\n"
1261                                         "For symbolic views (--sort has sym):\n\n"
1262                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1263                                         "<-            Zoom out\n"
1264                                         "a             Annotate current symbol\n"
1265                                         "C             Collapse all callchains\n"
1266                                         "E             Expand all callchains\n"
1267                                         "d             Zoom into current DSO\n"
1268                                         "t             Zoom into current Thread\n"
1269                                         "r             Run available scripts('perf report' only)\n"
1270                                         "P             Print histograms to perf.hist.N\n"
1271                                         "V             Verbose (DSO names in callchains, etc)\n"
1272                                         "/             Filter symbol by name");
1273                         continue;
1274                 case K_ENTER:
1275                 case K_RIGHT:
1276                         /* menu */
1277                         break;
1278                 case K_LEFT: {
1279                         const void *top;
1280
1281                         if (pstack__empty(fstack)) {
1282                                 /*
1283                                  * Go back to the perf_evsel_menu__run or other user
1284                                  */
1285                                 if (left_exits)
1286                                         goto out_free_stack;
1287                                 continue;
1288                         }
1289                         top = pstack__pop(fstack);
1290                         if (top == &browser->hists->dso_filter)
1291                                 goto zoom_out_dso;
1292                         if (top == &browser->hists->thread_filter)
1293                                 goto zoom_out_thread;
1294                         continue;
1295                 }
1296                 case K_ESC:
1297                         if (!left_exits &&
1298                             !ui_browser__dialog_yesno(&browser->b,
1299                                                "Do you really want to exit?"))
1300                                 continue;
1301                         /* Fall thru */
1302                 case 'q':
1303                 case CTRL('c'):
1304                         goto out_free_stack;
1305                 default:
1306                         continue;
1307                 }
1308
1309                 if (!browser->has_symbols)
1310                         goto add_exit_option;
1311
1312                 if (sort__branch_mode == 1) {
1313                         bi = browser->he_selection->branch_info;
1314                         if (browser->selection != NULL &&
1315                             bi &&
1316                             bi->from.sym != NULL &&
1317                             !bi->from.map->dso->annotate_warned &&
1318                                 asprintf(&options[nr_options], "Annotate %s",
1319                                          bi->from.sym->name) > 0)
1320                                 annotate_f = nr_options++;
1321
1322                         if (browser->selection != NULL &&
1323                             bi &&
1324                             bi->to.sym != NULL &&
1325                             !bi->to.map->dso->annotate_warned &&
1326                             (bi->to.sym != bi->from.sym ||
1327                              bi->to.map->dso != bi->from.map->dso) &&
1328                                 asprintf(&options[nr_options], "Annotate %s",
1329                                          bi->to.sym->name) > 0)
1330                                 annotate_t = nr_options++;
1331                 } else {
1332
1333                         if (browser->selection != NULL &&
1334                             browser->selection->sym != NULL &&
1335                             !browser->selection->map->dso->annotate_warned &&
1336                                 asprintf(&options[nr_options], "Annotate %s",
1337                                          browser->selection->sym->name) > 0)
1338                                 annotate = nr_options++;
1339                 }
1340
1341                 if (thread != NULL &&
1342                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1343                              (browser->hists->thread_filter ? "out of" : "into"),
1344                              (thread->comm_set ? thread->comm : ""),
1345                              thread->pid) > 0)
1346                         zoom_thread = nr_options++;
1347
1348                 if (dso != NULL &&
1349                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1350                              (browser->hists->dso_filter ? "out of" : "into"),
1351                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1352                         zoom_dso = nr_options++;
1353
1354                 if (browser->selection != NULL &&
1355                     browser->selection->map != NULL &&
1356                     asprintf(&options[nr_options], "Browse map details") > 0)
1357                         browse_map = nr_options++;
1358
1359                 /* perf script support */
1360                 if (browser->he_selection) {
1361                         struct symbol *sym;
1362
1363                         if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1364                                 browser->he_selection->thread->comm) > 0)
1365                                 scripts_comm = nr_options++;
1366
1367                         sym = browser->he_selection->ms.sym;
1368                         if (sym && sym->namelen &&
1369                                 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1370                                                 sym->name) > 0)
1371                                 scripts_symbol = nr_options++;
1372                 }
1373
1374                 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1375                         scripts_all = nr_options++;
1376
1377 add_exit_option:
1378                 options[nr_options++] = (char *)"Exit";
1379 retry_popup_menu:
1380                 choice = ui__popup_menu(nr_options, options);
1381
1382                 if (choice == nr_options - 1)
1383                         break;
1384
1385                 if (choice == -1) {
1386                         free_popup_options(options, nr_options - 1);
1387                         continue;
1388                 }
1389
1390                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1391                         struct hist_entry *he;
1392                         int err;
1393 do_annotate:
1394                         if (!objdump_path && perf_session_env__lookup_objdump(env))
1395                                 continue;
1396
1397                         he = hist_browser__selected_entry(browser);
1398                         if (he == NULL)
1399                                 continue;
1400
1401                         /*
1402                          * we stash the branch_info symbol + map into the
1403                          * the ms so we don't have to rewrite all the annotation
1404                          * code to use branch_info.
1405                          * in branch mode, the ms struct is not used
1406                          */
1407                         if (choice == annotate_f) {
1408                                 he->ms.sym = he->branch_info->from.sym;
1409                                 he->ms.map = he->branch_info->from.map;
1410                         }  else if (choice == annotate_t) {
1411                                 he->ms.sym = he->branch_info->to.sym;
1412                                 he->ms.map = he->branch_info->to.map;
1413                         }
1414
1415                         /*
1416                          * Don't let this be freed, say, by hists__decay_entry.
1417                          */
1418                         he->used = true;
1419                         err = hist_entry__tui_annotate(he, evsel->idx, hbt);
1420                         he->used = false;
1421                         /*
1422                          * offer option to annotate the other branch source or target
1423                          * (if they exists) when returning from annotate
1424                          */
1425                         if ((err == 'q' || err == CTRL('c'))
1426                             && annotate_t != -2 && annotate_f != -2)
1427                                 goto retry_popup_menu;
1428
1429                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1430                         if (err)
1431                                 ui_browser__handle_resize(&browser->b);
1432
1433                 } else if (choice == browse_map)
1434                         map__browse(browser->selection->map);
1435                 else if (choice == zoom_dso) {
1436 zoom_dso:
1437                         if (browser->hists->dso_filter) {
1438                                 pstack__remove(fstack, &browser->hists->dso_filter);
1439 zoom_out_dso:
1440                                 ui_helpline__pop();
1441                                 browser->hists->dso_filter = NULL;
1442                                 sort_dso.elide = false;
1443                         } else {
1444                                 if (dso == NULL)
1445                                         continue;
1446                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1447                                                    dso->kernel ? "the Kernel" : dso->short_name);
1448                                 browser->hists->dso_filter = dso;
1449                                 sort_dso.elide = true;
1450                                 pstack__push(fstack, &browser->hists->dso_filter);
1451                         }
1452                         hists__filter_by_dso(hists);
1453                         hist_browser__reset(browser);
1454                 } else if (choice == zoom_thread) {
1455 zoom_thread:
1456                         if (browser->hists->thread_filter) {
1457                                 pstack__remove(fstack, &browser->hists->thread_filter);
1458 zoom_out_thread:
1459                                 ui_helpline__pop();
1460                                 browser->hists->thread_filter = NULL;
1461                                 sort_thread.elide = false;
1462                         } else {
1463                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1464                                                    thread->comm_set ? thread->comm : "",
1465                                                    thread->pid);
1466                                 browser->hists->thread_filter = thread;
1467                                 sort_thread.elide = true;
1468                                 pstack__push(fstack, &browser->hists->thread_filter);
1469                         }
1470                         hists__filter_by_thread(hists);
1471                         hist_browser__reset(browser);
1472                 }
1473                 /* perf scripts support */
1474                 else if (choice == scripts_all || choice == scripts_comm ||
1475                                 choice == scripts_symbol) {
1476 do_scripts:
1477                         memset(script_opt, 0, 64);
1478
1479                         if (choice == scripts_comm)
1480                                 sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm);
1481
1482                         if (choice == scripts_symbol)
1483                                 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1484
1485                         script_browse(script_opt);
1486                 }
1487         }
1488 out_free_stack:
1489         pstack__delete(fstack);
1490 out:
1491         hist_browser__delete(browser);
1492         free_popup_options(options, nr_options - 1);
1493         return key;
1494 }
1495
1496 struct perf_evsel_menu {
1497         struct ui_browser b;
1498         struct perf_evsel *selection;
1499         bool lost_events, lost_events_warned;
1500         struct perf_session_env *env;
1501 };
1502
1503 static void perf_evsel_menu__write(struct ui_browser *browser,
1504                                    void *entry, int row)
1505 {
1506         struct perf_evsel_menu *menu = container_of(browser,
1507                                                     struct perf_evsel_menu, b);
1508         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1509         bool current_entry = ui_browser__is_current_entry(browser, row);
1510         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1511         const char *ev_name = perf_evsel__name(evsel);
1512         char bf[256], unit;
1513         const char *warn = " ";
1514         size_t printed;
1515
1516         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1517                                                        HE_COLORSET_NORMAL);
1518
1519         nr_events = convert_unit(nr_events, &unit);
1520         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1521                            unit, unit == ' ' ? "" : " ", ev_name);
1522         slsmg_printf("%s", bf);
1523
1524         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1525         if (nr_events != 0) {
1526                 menu->lost_events = true;
1527                 if (!current_entry)
1528                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1529                 nr_events = convert_unit(nr_events, &unit);
1530                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1531                                      nr_events, unit, unit == ' ' ? "" : " ");
1532                 warn = bf;
1533         }
1534
1535         slsmg_write_nstring(warn, browser->width - printed);
1536
1537         if (current_entry)
1538                 menu->selection = evsel;
1539 }
1540
1541 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1542                                 int nr_events, const char *help,
1543                                 struct hist_browser_timer *hbt)
1544 {
1545         struct perf_evlist *evlist = menu->b.priv;
1546         struct perf_evsel *pos;
1547         const char *ev_name, *title = "Available samples";
1548         int delay_secs = hbt ? hbt->refresh : 0;
1549         int key;
1550
1551         if (ui_browser__show(&menu->b, title,
1552                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1553                 return -1;
1554
1555         while (1) {
1556                 key = ui_browser__run(&menu->b, delay_secs);
1557
1558                 switch (key) {
1559                 case K_TIMER:
1560                         hbt->timer(hbt->arg);
1561
1562                         if (!menu->lost_events_warned && menu->lost_events) {
1563                                 ui_browser__warn_lost_events(&menu->b);
1564                                 menu->lost_events_warned = true;
1565                         }
1566                         continue;
1567                 case K_RIGHT:
1568                 case K_ENTER:
1569                         if (!menu->selection)
1570                                 continue;
1571                         pos = menu->selection;
1572 browse_hists:
1573                         perf_evlist__set_selected(evlist, pos);
1574                         /*
1575                          * Give the calling tool a chance to populate the non
1576                          * default evsel resorted hists tree.
1577                          */
1578                         if (hbt)
1579                                 hbt->timer(hbt->arg);
1580                         ev_name = perf_evsel__name(pos);
1581                         key = perf_evsel__hists_browse(pos, nr_events, help,
1582                                                        ev_name, true, hbt,
1583                                                        menu->env);
1584                         ui_browser__show_title(&menu->b, title);
1585                         switch (key) {
1586                         case K_TAB:
1587                                 if (pos->node.next == &evlist->entries)
1588                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1589                                 else
1590                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1591                                 goto browse_hists;
1592                         case K_UNTAB:
1593                                 if (pos->node.prev == &evlist->entries)
1594                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1595                                 else
1596                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1597                                 goto browse_hists;
1598                         case K_ESC:
1599                                 if (!ui_browser__dialog_yesno(&menu->b,
1600                                                 "Do you really want to exit?"))
1601                                         continue;
1602                                 /* Fall thru */
1603                         case 'q':
1604                         case CTRL('c'):
1605                                 goto out;
1606                         default:
1607                                 continue;
1608                         }
1609                 case K_LEFT:
1610                         continue;
1611                 case K_ESC:
1612                         if (!ui_browser__dialog_yesno(&menu->b,
1613                                                "Do you really want to exit?"))
1614                                 continue;
1615                         /* Fall thru */
1616                 case 'q':
1617                 case CTRL('c'):
1618                         goto out;
1619                 default:
1620                         continue;
1621                 }
1622         }
1623
1624 out:
1625         ui_browser__hide(&menu->b);
1626         return key;
1627 }
1628
1629 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1630                                            const char *help,
1631                                            struct hist_browser_timer *hbt,
1632                                            struct perf_session_env *env)
1633 {
1634         struct perf_evsel *pos;
1635         struct perf_evsel_menu menu = {
1636                 .b = {
1637                         .entries    = &evlist->entries,
1638                         .refresh    = ui_browser__list_head_refresh,
1639                         .seek       = ui_browser__list_head_seek,
1640                         .write      = perf_evsel_menu__write,
1641                         .nr_entries = evlist->nr_entries,
1642                         .priv       = evlist,
1643                 },
1644                 .env = env,
1645         };
1646
1647         ui_helpline__push("Press ESC to exit");
1648
1649         list_for_each_entry(pos, &evlist->entries, node) {
1650                 const char *ev_name = perf_evsel__name(pos);
1651                 size_t line_len = strlen(ev_name) + 7;
1652
1653                 if (menu.b.width < line_len)
1654                         menu.b.width = line_len;
1655         }
1656
1657         return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt);
1658 }
1659
1660 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1661                                   struct hist_browser_timer *hbt,
1662                                   struct perf_session_env *env)
1663 {
1664         if (evlist->nr_entries == 1) {
1665                 struct perf_evsel *first = list_entry(evlist->entries.next,
1666                                                       struct perf_evsel, node);
1667                 const char *ev_name = perf_evsel__name(first);
1668                 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1669                                                 ev_name, false, hbt, env);
1670         }
1671
1672         return __perf_evlist__tui_browse_hists(evlist, help, hbt, env);
1673 }