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