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