]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - tools/perf/ui/browsers/hists.c
perf hists browser: Add back callchain folding symbol
[~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
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
1145         if (browser == NULL)
1146                 return -1;
1147
1148         fstack = pstack__new(2);
1149         if (fstack == NULL)
1150                 goto out;
1151
1152         ui_helpline__push(helpline);
1153
1154         memset(options, 0, sizeof(options));
1155
1156         while (1) {
1157                 const struct thread *thread = NULL;
1158                 const struct dso *dso = NULL;
1159                 int choice = 0,
1160                     annotate = -2, zoom_dso = -2, zoom_thread = -2,
1161                     annotate_f = -2, annotate_t = -2, browse_map = -2;
1162
1163                 nr_options = 0;
1164
1165                 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
1166
1167                 if (browser->he_selection != NULL) {
1168                         thread = hist_browser__selected_thread(browser);
1169                         dso = browser->selection->map ? browser->selection->map->dso : NULL;
1170                 }
1171                 switch (key) {
1172                 case K_TAB:
1173                 case K_UNTAB:
1174                         if (nr_events == 1)
1175                                 continue;
1176                         /*
1177                          * Exit the browser, let hists__browser_tree
1178                          * go to the next or previous
1179                          */
1180                         goto out_free_stack;
1181                 case 'a':
1182                         if (!browser->has_symbols) {
1183                                 ui_browser__warning(&browser->b, delay_secs * 2,
1184                         "Annotation is only available for symbolic views, "
1185                         "include \"sym*\" in --sort to use it.");
1186                                 continue;
1187                         }
1188
1189                         if (browser->selection == NULL ||
1190                             browser->selection->sym == NULL ||
1191                             browser->selection->map->dso->annotate_warned)
1192                                 continue;
1193                         goto do_annotate;
1194                 case 'P':
1195                         hist_browser__dump(browser);
1196                         continue;
1197                 case 'd':
1198                         goto zoom_dso;
1199                 case 'V':
1200                         browser->show_dso = !browser->show_dso;
1201                         continue;
1202                 case 't':
1203                         goto zoom_thread;
1204                 case '/':
1205                         if (ui_browser__input_window("Symbol to show",
1206                                         "Please enter the name of symbol you want to see",
1207                                         buf, "ENTER: OK, ESC: Cancel",
1208                                         delay_secs * 2) == K_ENTER) {
1209                                 hists->symbol_filter_str = *buf ? buf : NULL;
1210                                 hists__filter_by_symbol(hists);
1211                                 hist_browser__reset(browser);
1212                         }
1213                         continue;
1214                 case K_F1:
1215                 case 'h':
1216                 case '?':
1217                         ui_browser__help_window(&browser->b,
1218                                         "h/?/F1        Show this window\n"
1219                                         "UP/DOWN/PGUP\n"
1220                                         "PGDN/SPACE    Navigate\n"
1221                                         "q/ESC/CTRL+C  Exit browser\n\n"
1222                                         "For multiple event sessions:\n\n"
1223                                         "TAB/UNTAB Switch events\n\n"
1224                                         "For symbolic views (--sort has sym):\n\n"
1225                                         "->            Zoom into DSO/Threads & Annotate current symbol\n"
1226                                         "<-            Zoom out\n"
1227                                         "a             Annotate current symbol\n"
1228                                         "C             Collapse all callchains\n"
1229                                         "E             Expand all callchains\n"
1230                                         "d             Zoom into current DSO\n"
1231                                         "t             Zoom into current Thread\n"
1232                                         "P             Print histograms to perf.hist.N\n"
1233                                         "V             Verbose (DSO names in callchains, etc)\n"
1234                                         "/             Filter symbol by name");
1235                         continue;
1236                 case K_ENTER:
1237                 case K_RIGHT:
1238                         /* menu */
1239                         break;
1240                 case K_LEFT: {
1241                         const void *top;
1242
1243                         if (pstack__empty(fstack)) {
1244                                 /*
1245                                  * Go back to the perf_evsel_menu__run or other user
1246                                  */
1247                                 if (left_exits)
1248                                         goto out_free_stack;
1249                                 continue;
1250                         }
1251                         top = pstack__pop(fstack);
1252                         if (top == &browser->hists->dso_filter)
1253                                 goto zoom_out_dso;
1254                         if (top == &browser->hists->thread_filter)
1255                                 goto zoom_out_thread;
1256                         continue;
1257                 }
1258                 case K_ESC:
1259                         if (!left_exits &&
1260                             !ui_browser__dialog_yesno(&browser->b,
1261                                                "Do you really want to exit?"))
1262                                 continue;
1263                         /* Fall thru */
1264                 case 'q':
1265                 case CTRL('c'):
1266                         goto out_free_stack;
1267                 default:
1268                         continue;
1269                 }
1270
1271                 if (!browser->has_symbols)
1272                         goto add_exit_option;
1273
1274                 if (sort__branch_mode == 1) {
1275                         bi = browser->he_selection->branch_info;
1276                         if (browser->selection != NULL &&
1277                             bi &&
1278                             bi->from.sym != NULL &&
1279                             !bi->from.map->dso->annotate_warned &&
1280                                 asprintf(&options[nr_options], "Annotate %s",
1281                                          bi->from.sym->name) > 0)
1282                                 annotate_f = nr_options++;
1283
1284                         if (browser->selection != NULL &&
1285                             bi &&
1286                             bi->to.sym != NULL &&
1287                             !bi->to.map->dso->annotate_warned &&
1288                             (bi->to.sym != bi->from.sym ||
1289                              bi->to.map->dso != bi->from.map->dso) &&
1290                                 asprintf(&options[nr_options], "Annotate %s",
1291                                          bi->to.sym->name) > 0)
1292                                 annotate_t = nr_options++;
1293                 } else {
1294
1295                         if (browser->selection != NULL &&
1296                             browser->selection->sym != NULL &&
1297                             !browser->selection->map->dso->annotate_warned &&
1298                                 asprintf(&options[nr_options], "Annotate %s",
1299                                          browser->selection->sym->name) > 0)
1300                                 annotate = nr_options++;
1301                 }
1302
1303                 if (thread != NULL &&
1304                     asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1305                              (browser->hists->thread_filter ? "out of" : "into"),
1306                              (thread->comm_set ? thread->comm : ""),
1307                              thread->pid) > 0)
1308                         zoom_thread = nr_options++;
1309
1310                 if (dso != NULL &&
1311                     asprintf(&options[nr_options], "Zoom %s %s DSO",
1312                              (browser->hists->dso_filter ? "out of" : "into"),
1313                              (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1314                         zoom_dso = nr_options++;
1315
1316                 if (browser->selection != NULL &&
1317                     browser->selection->map != NULL &&
1318                     asprintf(&options[nr_options], "Browse map details") > 0)
1319                         browse_map = nr_options++;
1320 add_exit_option:
1321                 options[nr_options++] = (char *)"Exit";
1322 retry_popup_menu:
1323                 choice = ui__popup_menu(nr_options, options);
1324
1325                 if (choice == nr_options - 1)
1326                         break;
1327
1328                 if (choice == -1) {
1329                         free_popup_options(options, nr_options - 1);
1330                         continue;
1331                 }
1332
1333                 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1334                         struct hist_entry *he;
1335                         int err;
1336 do_annotate:
1337                         he = hist_browser__selected_entry(browser);
1338                         if (he == NULL)
1339                                 continue;
1340
1341                         /*
1342                          * we stash the branch_info symbol + map into the
1343                          * the ms so we don't have to rewrite all the annotation
1344                          * code to use branch_info.
1345                          * in branch mode, the ms struct is not used
1346                          */
1347                         if (choice == annotate_f) {
1348                                 he->ms.sym = he->branch_info->from.sym;
1349                                 he->ms.map = he->branch_info->from.map;
1350                         }  else if (choice == annotate_t) {
1351                                 he->ms.sym = he->branch_info->to.sym;
1352                                 he->ms.map = he->branch_info->to.map;
1353                         }
1354
1355                         /*
1356                          * Don't let this be freed, say, by hists__decay_entry.
1357                          */
1358                         he->used = true;
1359                         err = hist_entry__tui_annotate(he, evsel->idx,
1360                                                        timer, arg, delay_secs);
1361                         he->used = false;
1362                         /*
1363                          * offer option to annotate the other branch source or target
1364                          * (if they exists) when returning from annotate
1365                          */
1366                         if ((err == 'q' || err == CTRL('c'))
1367                             && annotate_t != -2 && annotate_f != -2)
1368                                 goto retry_popup_menu;
1369
1370                         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1371                         if (err)
1372                                 ui_browser__handle_resize(&browser->b);
1373
1374                 } else if (choice == browse_map)
1375                         map__browse(browser->selection->map);
1376                 else if (choice == zoom_dso) {
1377 zoom_dso:
1378                         if (browser->hists->dso_filter) {
1379                                 pstack__remove(fstack, &browser->hists->dso_filter);
1380 zoom_out_dso:
1381                                 ui_helpline__pop();
1382                                 browser->hists->dso_filter = NULL;
1383                                 sort_dso.elide = false;
1384                         } else {
1385                                 if (dso == NULL)
1386                                         continue;
1387                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1388                                                    dso->kernel ? "the Kernel" : dso->short_name);
1389                                 browser->hists->dso_filter = dso;
1390                                 sort_dso.elide = true;
1391                                 pstack__push(fstack, &browser->hists->dso_filter);
1392                         }
1393                         hists__filter_by_dso(hists);
1394                         hist_browser__reset(browser);
1395                 } else if (choice == zoom_thread) {
1396 zoom_thread:
1397                         if (browser->hists->thread_filter) {
1398                                 pstack__remove(fstack, &browser->hists->thread_filter);
1399 zoom_out_thread:
1400                                 ui_helpline__pop();
1401                                 browser->hists->thread_filter = NULL;
1402                                 sort_thread.elide = false;
1403                         } else {
1404                                 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1405                                                    thread->comm_set ? thread->comm : "",
1406                                                    thread->pid);
1407                                 browser->hists->thread_filter = thread;
1408                                 sort_thread.elide = true;
1409                                 pstack__push(fstack, &browser->hists->thread_filter);
1410                         }
1411                         hists__filter_by_thread(hists);
1412                         hist_browser__reset(browser);
1413                 }
1414         }
1415 out_free_stack:
1416         pstack__delete(fstack);
1417 out:
1418         hist_browser__delete(browser);
1419         free_popup_options(options, nr_options - 1);
1420         return key;
1421 }
1422
1423 struct perf_evsel_menu {
1424         struct ui_browser b;
1425         struct perf_evsel *selection;
1426         bool lost_events, lost_events_warned;
1427 };
1428
1429 static void perf_evsel_menu__write(struct ui_browser *browser,
1430                                    void *entry, int row)
1431 {
1432         struct perf_evsel_menu *menu = container_of(browser,
1433                                                     struct perf_evsel_menu, b);
1434         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1435         bool current_entry = ui_browser__is_current_entry(browser, row);
1436         unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1437         const char *ev_name = perf_evsel__name(evsel);
1438         char bf[256], unit;
1439         const char *warn = " ";
1440         size_t printed;
1441
1442         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1443                                                        HE_COLORSET_NORMAL);
1444
1445         nr_events = convert_unit(nr_events, &unit);
1446         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1447                            unit, unit == ' ' ? "" : " ", ev_name);
1448         slsmg_printf("%s", bf);
1449
1450         nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST];
1451         if (nr_events != 0) {
1452                 menu->lost_events = true;
1453                 if (!current_entry)
1454                         ui_browser__set_color(browser, HE_COLORSET_TOP);
1455                 nr_events = convert_unit(nr_events, &unit);
1456                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1457                                      nr_events, unit, unit == ' ' ? "" : " ");
1458                 warn = bf;
1459         }
1460
1461         slsmg_write_nstring(warn, browser->width - printed);
1462
1463         if (current_entry)
1464                 menu->selection = evsel;
1465 }
1466
1467 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1468                                 int nr_events, const char *help,
1469                                 void(*timer)(void *arg), void *arg, int delay_secs)
1470 {
1471         struct perf_evlist *evlist = menu->b.priv;
1472         struct perf_evsel *pos;
1473         const char *ev_name, *title = "Available samples";
1474         int key;
1475
1476         if (ui_browser__show(&menu->b, title,
1477                              "ESC: exit, ENTER|->: Browse histograms") < 0)
1478                 return -1;
1479
1480         while (1) {
1481                 key = ui_browser__run(&menu->b, delay_secs);
1482
1483                 switch (key) {
1484                 case K_TIMER:
1485                         timer(arg);
1486
1487                         if (!menu->lost_events_warned && menu->lost_events) {
1488                                 ui_browser__warn_lost_events(&menu->b);
1489                                 menu->lost_events_warned = true;
1490                         }
1491                         continue;
1492                 case K_RIGHT:
1493                 case K_ENTER:
1494                         if (!menu->selection)
1495                                 continue;
1496                         pos = menu->selection;
1497 browse_hists:
1498                         perf_evlist__set_selected(evlist, pos);
1499                         /*
1500                          * Give the calling tool a chance to populate the non
1501                          * default evsel resorted hists tree.
1502                          */
1503                         if (timer)
1504                                 timer(arg);
1505                         ev_name = perf_evsel__name(pos);
1506                         key = perf_evsel__hists_browse(pos, nr_events, help,
1507                                                        ev_name, true, timer,
1508                                                        arg, delay_secs);
1509                         ui_browser__show_title(&menu->b, title);
1510                         switch (key) {
1511                         case K_TAB:
1512                                 if (pos->node.next == &evlist->entries)
1513                                         pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1514                                 else
1515                                         pos = list_entry(pos->node.next, struct perf_evsel, node);
1516                                 goto browse_hists;
1517                         case K_UNTAB:
1518                                 if (pos->node.prev == &evlist->entries)
1519                                         pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1520                                 else
1521                                         pos = list_entry(pos->node.prev, struct perf_evsel, node);
1522                                 goto browse_hists;
1523                         case K_ESC:
1524                                 if (!ui_browser__dialog_yesno(&menu->b,
1525                                                 "Do you really want to exit?"))
1526                                         continue;
1527                                 /* Fall thru */
1528                         case 'q':
1529                         case CTRL('c'):
1530                                 goto out;
1531                         default:
1532                                 continue;
1533                         }
1534                 case K_LEFT:
1535                         continue;
1536                 case K_ESC:
1537                         if (!ui_browser__dialog_yesno(&menu->b,
1538                                                "Do you really want to exit?"))
1539                                 continue;
1540                         /* Fall thru */
1541                 case 'q':
1542                 case CTRL('c'):
1543                         goto out;
1544                 default:
1545                         continue;
1546                 }
1547         }
1548
1549 out:
1550         ui_browser__hide(&menu->b);
1551         return key;
1552 }
1553
1554 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1555                                            const char *help,
1556                                            void(*timer)(void *arg), void *arg,
1557                                            int delay_secs)
1558 {
1559         struct perf_evsel *pos;
1560         struct perf_evsel_menu menu = {
1561                 .b = {
1562                         .entries    = &evlist->entries,
1563                         .refresh    = ui_browser__list_head_refresh,
1564                         .seek       = ui_browser__list_head_seek,
1565                         .write      = perf_evsel_menu__write,
1566                         .nr_entries = evlist->nr_entries,
1567                         .priv       = evlist,
1568                 },
1569         };
1570
1571         ui_helpline__push("Press ESC to exit");
1572
1573         list_for_each_entry(pos, &evlist->entries, node) {
1574                 const char *ev_name = perf_evsel__name(pos);
1575                 size_t line_len = strlen(ev_name) + 7;
1576
1577                 if (menu.b.width < line_len)
1578                         menu.b.width = line_len;
1579         }
1580
1581         return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1582                                     arg, delay_secs);
1583 }
1584
1585 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1586                                   void(*timer)(void *arg), void *arg,
1587                                   int delay_secs)
1588 {
1589         if (evlist->nr_entries == 1) {
1590                 struct perf_evsel *first = list_entry(evlist->entries.next,
1591                                                       struct perf_evsel, node);
1592                 const char *ev_name = perf_evsel__name(first);
1593                 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1594                                                 ev_name, false, timer, arg,
1595                                                 delay_secs);
1596         }
1597
1598         return __perf_evlist__tui_browse_hists(evlist, help,
1599                                                timer, arg, delay_secs);
1600 }