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