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