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