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