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