d26b925e3d7f46afad89b08ebb38a6cae34e9479
[linux-2.6-block.git] / tools / perf / ui / browsers / hists.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <linux/rbtree.h>
9 #include <linux/string.h>
10 #include <sys/ttydefaults.h>
11 #include <linux/time64.h>
12 #include <linux/zalloc.h>
13
14 #include "../../util/debug.h"
15 #include "../../util/dso.h"
16 #include "../../util/callchain.h"
17 #include "../../util/evsel.h"
18 #include "../../util/evlist.h"
19 #include "../../util/header.h"
20 #include "../../util/hist.h"
21 #include "../../util/machine.h"
22 #include "../../util/map.h"
23 #include "../../util/maps.h"
24 #include "../../util/symbol.h"
25 #include "../../util/map_symbol.h"
26 #include "../../util/branch.h"
27 #include "../../util/pstack.h"
28 #include "../../util/sort.h"
29 #include "../../util/top.h"
30 #include "../../util/thread.h"
31 #include "../../util/block-info.h"
32 #include "../../util/util.h"
33 #include "../../arch/common.h"
34
35 #include "../browsers/hists.h"
36 #include "../helpline.h"
37 #include "../util.h"
38 #include "../ui.h"
39 #include "map.h"
40 #include "annotate.h"
41 #include "annotate-data.h"
42 #include "srcline.h"
43 #include "string2.h"
44 #include "units.h"
45 #include "time-utils.h"
46
47 #include <linux/ctype.h>
48
49 extern void hist_browser__init_hpp(void);
50
51 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size);
52 static void hist_browser__update_nr_entries(struct hist_browser *hb);
53
54 static struct rb_node *hists__filter_entries(struct rb_node *nd,
55                                              float min_pcnt);
56
57 static bool hist_browser__has_filter(struct hist_browser *hb)
58 {
59         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
60 }
61
62 static int hist_browser__get_folding(struct hist_browser *browser)
63 {
64         struct rb_node *nd;
65         struct hists *hists = browser->hists;
66         int unfolded_rows = 0;
67
68         for (nd = rb_first_cached(&hists->entries);
69              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
70              nd = rb_hierarchy_next(nd)) {
71                 struct hist_entry *he =
72                         rb_entry(nd, struct hist_entry, rb_node);
73
74                 if (he->leaf && he->unfolded)
75                         unfolded_rows += he->nr_rows;
76         }
77         return unfolded_rows;
78 }
79
80 static void hist_browser__set_title_space(struct hist_browser *hb)
81 {
82         struct ui_browser *browser = &hb->b;
83         struct hists *hists = hb->hists;
84         struct perf_hpp_list *hpp_list = hists->hpp_list;
85
86         browser->extra_title_lines = hb->show_headers ? hpp_list->nr_header_lines : 0;
87 }
88
89 static u32 hist_browser__nr_entries(struct hist_browser *hb)
90 {
91         u32 nr_entries;
92
93         if (symbol_conf.report_hierarchy)
94                 nr_entries = hb->nr_hierarchy_entries;
95         else if (hist_browser__has_filter(hb))
96                 nr_entries = hb->nr_non_filtered_entries;
97         else
98                 nr_entries = hb->hists->nr_entries;
99
100         hb->nr_callchain_rows = hist_browser__get_folding(hb);
101         return nr_entries + hb->nr_callchain_rows;
102 }
103
104 static void hist_browser__update_rows(struct hist_browser *hb)
105 {
106         struct ui_browser *browser = &hb->b;
107         struct hists *hists = hb->hists;
108         struct perf_hpp_list *hpp_list = hists->hpp_list;
109         u16 index_row;
110
111         if (!hb->show_headers) {
112                 browser->rows += browser->extra_title_lines;
113                 browser->extra_title_lines = 0;
114                 return;
115         }
116
117         browser->extra_title_lines = hpp_list->nr_header_lines;
118         browser->rows -= browser->extra_title_lines;
119         /*
120          * Verify if we were at the last line and that line isn't
121          * visible because we now show the header line(s).
122          */
123         index_row = browser->index - browser->top_idx;
124         if (index_row >= browser->rows)
125                 browser->index -= index_row - browser->rows + 1;
126 }
127
128 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
129 {
130         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
131
132         /* 3 == +/- toggle symbol before actual hist_entry rendering */
133         browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
134         /*
135          * FIXME: Just keeping existing behaviour, but this really should be
136          *        before updating browser->width, as it will invalidate the
137          *        calculation above. Fix this and the fallout in another
138          *        changeset.
139          */
140         ui_browser__refresh_dimensions(browser);
141 }
142
143 static void hist_browser__reset(struct hist_browser *browser)
144 {
145         /*
146          * The hists__remove_entry_filter() already folds non-filtered
147          * entries so we can assume it has 0 callchain rows.
148          */
149         browser->nr_callchain_rows = 0;
150
151         hist_browser__update_nr_entries(browser);
152         browser->b.nr_entries = hist_browser__nr_entries(browser);
153         hist_browser__refresh_dimensions(&browser->b);
154         ui_browser__reset_index(&browser->b);
155 }
156
157 static char tree__folded_sign(bool unfolded)
158 {
159         return unfolded ? '-' : '+';
160 }
161
162 static char hist_entry__folded(const struct hist_entry *he)
163 {
164         return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
165 }
166
167 static char callchain_list__folded(const struct callchain_list *cl)
168 {
169         return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
170 }
171
172 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
173 {
174         cl->unfolded = unfold ? cl->has_children : false;
175 }
176
177 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
178 {
179         int n = 0;
180         struct rb_node *nd;
181
182         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
183                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
184                 struct callchain_list *chain;
185                 char folded_sign = ' '; /* No children */
186
187                 list_for_each_entry(chain, &child->val, list) {
188                         ++n;
189
190                         /* We need this because we may not have children */
191                         folded_sign = callchain_list__folded(chain);
192                         if (folded_sign == '+')
193                                 break;
194                 }
195
196                 if (folded_sign == '-') /* Have children and they're unfolded */
197                         n += callchain_node__count_rows_rb_tree(child);
198         }
199
200         return n;
201 }
202
203 static int callchain_node__count_flat_rows(struct callchain_node *node)
204 {
205         struct callchain_list *chain;
206         char folded_sign = 0;
207         int n = 0;
208
209         list_for_each_entry(chain, &node->parent_val, list) {
210                 if (!folded_sign) {
211                         /* only check first chain list entry */
212                         folded_sign = callchain_list__folded(chain);
213                         if (folded_sign == '+')
214                                 return 1;
215                 }
216                 n++;
217         }
218
219         list_for_each_entry(chain, &node->val, list) {
220                 if (!folded_sign) {
221                         /* node->parent_val list might be empty */
222                         folded_sign = callchain_list__folded(chain);
223                         if (folded_sign == '+')
224                                 return 1;
225                 }
226                 n++;
227         }
228
229         return n;
230 }
231
232 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
233 {
234         return 1;
235 }
236
237 static int callchain_node__count_rows(struct callchain_node *node)
238 {
239         struct callchain_list *chain;
240         bool unfolded = false;
241         int n = 0;
242
243         if (callchain_param.mode == CHAIN_FLAT)
244                 return callchain_node__count_flat_rows(node);
245         else if (callchain_param.mode == CHAIN_FOLDED)
246                 return callchain_node__count_folded_rows(node);
247
248         list_for_each_entry(chain, &node->val, list) {
249                 ++n;
250
251                 unfolded = chain->unfolded;
252         }
253
254         if (unfolded)
255                 n += callchain_node__count_rows_rb_tree(node);
256
257         return n;
258 }
259
260 static int callchain__count_rows(struct rb_root *chain)
261 {
262         struct rb_node *nd;
263         int n = 0;
264
265         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
266                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
267                 n += callchain_node__count_rows(node);
268         }
269
270         return n;
271 }
272
273 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
274                                 bool include_children)
275 {
276         int count = 0;
277         struct rb_node *node;
278         struct hist_entry *child;
279
280         if (he->leaf)
281                 return callchain__count_rows(&he->sorted_chain);
282
283         if (he->has_no_entry)
284                 return 1;
285
286         node = rb_first_cached(&he->hroot_out);
287         while (node) {
288                 float percent;
289
290                 child = rb_entry(node, struct hist_entry, rb_node);
291                 percent = hist_entry__get_percent_limit(child);
292
293                 if (!child->filtered && percent >= hb->min_pcnt) {
294                         count++;
295
296                         if (include_children && child->unfolded)
297                                 count += hierarchy_count_rows(hb, child, true);
298                 }
299
300                 node = rb_next(node);
301         }
302         return count;
303 }
304
305 static bool hist_entry__toggle_fold(struct hist_entry *he)
306 {
307         if (!he)
308                 return false;
309
310         if (!he->has_children)
311                 return false;
312
313         he->unfolded = !he->unfolded;
314         return true;
315 }
316
317 static bool callchain_list__toggle_fold(struct callchain_list *cl)
318 {
319         if (!cl)
320                 return false;
321
322         if (!cl->has_children)
323                 return false;
324
325         cl->unfolded = !cl->unfolded;
326         return true;
327 }
328
329 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
330 {
331         struct rb_node *nd = rb_first(&node->rb_root);
332
333         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
334                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
335                 struct callchain_list *chain;
336                 bool first = true;
337
338                 list_for_each_entry(chain, &child->val, list) {
339                         if (first) {
340                                 first = false;
341                                 chain->has_children = chain->list.next != &child->val ||
342                                                          !RB_EMPTY_ROOT(&child->rb_root);
343                         } else
344                                 chain->has_children = chain->list.next == &child->val &&
345                                                          !RB_EMPTY_ROOT(&child->rb_root);
346                 }
347
348                 callchain_node__init_have_children_rb_tree(child);
349         }
350 }
351
352 static void callchain_node__init_have_children(struct callchain_node *node,
353                                                bool has_sibling)
354 {
355         struct callchain_list *chain;
356
357         chain = list_entry(node->val.next, struct callchain_list, list);
358         chain->has_children = has_sibling;
359
360         if (!list_empty(&node->val)) {
361                 chain = list_entry(node->val.prev, struct callchain_list, list);
362                 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
363         }
364
365         callchain_node__init_have_children_rb_tree(node);
366 }
367
368 static void callchain__init_have_children(struct rb_root *root)
369 {
370         struct rb_node *nd = rb_first(root);
371         bool has_sibling = nd && rb_next(nd);
372
373         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
374                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
375                 callchain_node__init_have_children(node, has_sibling);
376                 if (callchain_param.mode == CHAIN_FLAT ||
377                     callchain_param.mode == CHAIN_FOLDED)
378                         callchain_node__make_parent_list(node);
379         }
380 }
381
382 static void hist_entry__init_have_children(struct hist_entry *he)
383 {
384         if (he->init_have_children)
385                 return;
386
387         if (he->leaf) {
388                 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
389                 callchain__init_have_children(&he->sorted_chain);
390         } else {
391                 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out.rb_root);
392         }
393
394         he->init_have_children = true;
395 }
396
397 static bool hist_browser__selection_has_children(struct hist_browser *browser)
398 {
399         struct hist_entry *he = browser->he_selection;
400         struct map_symbol *ms = browser->selection;
401
402         if (!he || !ms)
403                 return false;
404
405         if (ms == &he->ms)
406                return he->has_children;
407
408         return container_of(ms, struct callchain_list, ms)->has_children;
409 }
410
411 static bool hist_browser__selection_unfolded(struct hist_browser *browser)
412 {
413         struct hist_entry *he = browser->he_selection;
414         struct map_symbol *ms = browser->selection;
415
416         if (!he || !ms)
417                 return false;
418
419         if (ms == &he->ms)
420                return he->unfolded;
421
422         return container_of(ms, struct callchain_list, ms)->unfolded;
423 }
424
425 static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size)
426 {
427         struct hist_entry *he = browser->he_selection;
428         struct map_symbol *ms = browser->selection;
429         struct callchain_list *callchain_entry;
430
431         if (!he || !ms)
432                 return NULL;
433
434         if (ms == &he->ms) {
435                hist_entry__sym_snprintf(he, bf, size, 0);
436                return bf + 4; // skip the level, e.g. '[k] '
437         }
438
439         callchain_entry = container_of(ms, struct callchain_list, ms);
440         return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso);
441 }
442
443 static bool hist_browser__toggle_fold(struct hist_browser *browser)
444 {
445         struct hist_entry *he = browser->he_selection;
446         struct map_symbol *ms = browser->selection;
447         struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
448         bool has_children;
449
450         if (!he || !ms)
451                 return false;
452
453         if (ms == &he->ms)
454                 has_children = hist_entry__toggle_fold(he);
455         else
456                 has_children = callchain_list__toggle_fold(cl);
457
458         if (has_children) {
459                 int child_rows = 0;
460
461                 hist_entry__init_have_children(he);
462                 browser->b.nr_entries -= he->nr_rows;
463
464                 if (he->leaf)
465                         browser->nr_callchain_rows -= he->nr_rows;
466                 else
467                         browser->nr_hierarchy_entries -= he->nr_rows;
468
469                 if (symbol_conf.report_hierarchy)
470                         child_rows = hierarchy_count_rows(browser, he, true);
471
472                 if (he->unfolded) {
473                         if (he->leaf)
474                                 he->nr_rows = callchain__count_rows(
475                                                 &he->sorted_chain);
476                         else
477                                 he->nr_rows = hierarchy_count_rows(browser, he, false);
478
479                         /* account grand children */
480                         if (symbol_conf.report_hierarchy)
481                                 browser->b.nr_entries += child_rows - he->nr_rows;
482
483                         if (!he->leaf && he->nr_rows == 0) {
484                                 he->has_no_entry = true;
485                                 he->nr_rows = 1;
486                         }
487                 } else {
488                         if (symbol_conf.report_hierarchy)
489                                 browser->b.nr_entries -= child_rows - he->nr_rows;
490
491                         if (he->has_no_entry)
492                                 he->has_no_entry = false;
493
494                         he->nr_rows = 0;
495                 }
496
497                 browser->b.nr_entries += he->nr_rows;
498
499                 if (he->leaf)
500                         browser->nr_callchain_rows += he->nr_rows;
501                 else
502                         browser->nr_hierarchy_entries += he->nr_rows;
503
504                 return true;
505         }
506
507         /* If it doesn't have children, no toggling performed */
508         return false;
509 }
510
511 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
512 {
513         int n = 0;
514         struct rb_node *nd;
515
516         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
517                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
518                 struct callchain_list *chain;
519                 bool has_children = false;
520
521                 list_for_each_entry(chain, &child->val, list) {
522                         ++n;
523                         callchain_list__set_folding(chain, unfold);
524                         has_children = chain->has_children;
525                 }
526
527                 if (has_children)
528                         n += callchain_node__set_folding_rb_tree(child, unfold);
529         }
530
531         return n;
532 }
533
534 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
535 {
536         struct callchain_list *chain;
537         bool has_children = false;
538         int n = 0;
539
540         list_for_each_entry(chain, &node->val, list) {
541                 ++n;
542                 callchain_list__set_folding(chain, unfold);
543                 has_children = chain->has_children;
544         }
545
546         if (has_children)
547                 n += callchain_node__set_folding_rb_tree(node, unfold);
548
549         return n;
550 }
551
552 static int callchain__set_folding(struct rb_root *chain, bool unfold)
553 {
554         struct rb_node *nd;
555         int n = 0;
556
557         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
558                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
559                 n += callchain_node__set_folding(node, unfold);
560         }
561
562         return n;
563 }
564
565 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
566                                  bool unfold __maybe_unused)
567 {
568         float percent;
569         struct rb_node *nd;
570         struct hist_entry *child;
571         int n = 0;
572
573         for (nd = rb_first_cached(&he->hroot_out); nd; nd = rb_next(nd)) {
574                 child = rb_entry(nd, struct hist_entry, rb_node);
575                 percent = hist_entry__get_percent_limit(child);
576                 if (!child->filtered && percent >= hb->min_pcnt)
577                         n++;
578         }
579
580         return n;
581 }
582
583 static void hist_entry__set_folding(struct hist_entry *he,
584                                     struct hist_browser *hb, bool unfold)
585 {
586         hist_entry__init_have_children(he);
587         he->unfolded = unfold ? he->has_children : false;
588
589         if (he->has_children) {
590                 int n;
591
592                 if (he->leaf)
593                         n = callchain__set_folding(&he->sorted_chain, unfold);
594                 else
595                         n = hierarchy_set_folding(hb, he, unfold);
596
597                 he->nr_rows = unfold ? n : 0;
598         } else
599                 he->nr_rows = 0;
600 }
601
602 static void
603 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
604 {
605         struct rb_node *nd;
606         struct hist_entry *he;
607         double percent;
608
609         nd = rb_first_cached(&browser->hists->entries);
610         while (nd) {
611                 he = rb_entry(nd, struct hist_entry, rb_node);
612
613                 /* set folding state even if it's currently folded */
614                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
615
616                 hist_entry__set_folding(he, browser, unfold);
617
618                 percent = hist_entry__get_percent_limit(he);
619                 if (he->filtered || percent < browser->min_pcnt)
620                         continue;
621
622                 if (!he->depth || unfold)
623                         browser->nr_hierarchy_entries++;
624                 if (he->leaf)
625                         browser->nr_callchain_rows += he->nr_rows;
626                 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
627                         browser->nr_hierarchy_entries++;
628                         he->has_no_entry = true;
629                         he->nr_rows = 1;
630                 } else
631                         he->has_no_entry = false;
632         }
633 }
634
635 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
636 {
637         browser->nr_hierarchy_entries = 0;
638         browser->nr_callchain_rows = 0;
639         __hist_browser__set_folding(browser, unfold);
640
641         browser->b.nr_entries = hist_browser__nr_entries(browser);
642         /* Go to the start, we may be way after valid entries after a collapse */
643         ui_browser__reset_index(&browser->b);
644 }
645
646 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
647 {
648         if (!browser->he_selection)
649                 return;
650
651         if (unfold == browser->he_selection->unfolded)
652                 return;
653
654         hist_browser__toggle_fold(browser);
655 }
656
657 static void ui_browser__warn_lost_events(struct ui_browser *browser)
658 {
659         ui_browser__warning(browser, 4,
660                 "Events are being lost, check IO/CPU overload!\n\n"
661                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
662                 " perf top -r 80\n\n"
663                 "Or reduce the sampling frequency.");
664 }
665
666 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
667 {
668         return browser->title ? browser->title(browser, bf, size) : 0;
669 }
670
671 static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, size_t size, int key)
672 {
673         switch (key) {
674         case K_TIMER: {
675                 struct hist_browser_timer *hbt = browser->hbt;
676                 struct evsel *evsel = hists_to_evsel(browser->hists);
677                 u64 nr_entries;
678
679                 WARN_ON_ONCE(!hbt);
680
681                 if (hbt)
682                         hbt->timer(hbt->arg);
683
684                 if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy)
685                         hist_browser__update_nr_entries(browser);
686
687                 nr_entries = hist_browser__nr_entries(browser);
688                 ui_browser__update_nr_entries(&browser->b, nr_entries);
689
690                 if (warn_lost_event &&
691                     (evsel->evlist->stats.nr_lost_warned !=
692                      evsel->evlist->stats.nr_events[PERF_RECORD_LOST])) {
693                         evsel->evlist->stats.nr_lost_warned =
694                                 evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
695                         ui_browser__warn_lost_events(&browser->b);
696                 }
697
698                 hist_browser__title(browser, title, size);
699                 ui_browser__show_title(&browser->b, title);
700                 break;
701         }
702         case 'D': { /* Debug */
703                 struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node);
704                 static int seq;
705
706                 ui_helpline__pop();
707                 ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
708                                    seq++, browser->b.nr_entries, browser->hists->nr_entries,
709                                    browser->b.extra_title_lines, browser->b.rows,
710                                    browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows);
711         }
712                 break;
713         case 'C':
714                 /* Collapse the whole world. */
715                 hist_browser__set_folding(browser, false);
716                 break;
717         case 'c':
718                 /* Collapse the selected entry. */
719                 hist_browser__set_folding_selected(browser, false);
720                 break;
721         case 'E':
722                 /* Expand the whole world. */
723                 hist_browser__set_folding(browser, true);
724                 break;
725         case 'e':
726                 /* Toggle expand/collapse the selected entry. */
727                 hist_browser__toggle_fold(browser);
728                 break;
729         case 'H':
730                 browser->show_headers = !browser->show_headers;
731                 hist_browser__update_rows(browser);
732                 break;
733         case '+':
734                 if (hist_browser__toggle_fold(browser))
735                         break;
736                 /* fall thru */
737         default:
738                 return -1;
739         }
740
741         return 0;
742 }
743
744 int hist_browser__run(struct hist_browser *browser, const char *help,
745                       bool warn_lost_event, int key)
746 {
747         char title[160];
748         struct hist_browser_timer *hbt = browser->hbt;
749         int delay_secs = hbt ? hbt->refresh : 0;
750
751         browser->b.entries = &browser->hists->entries;
752         browser->b.nr_entries = hist_browser__nr_entries(browser);
753
754         hist_browser__title(browser, title, sizeof(title));
755
756         if (ui_browser__show(&browser->b, title, "%s", help) < 0)
757                 return -1;
758
759         if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
760                 goto out;
761
762         while (1) {
763                 key = ui_browser__run(&browser->b, delay_secs);
764
765                 if (hist_browser__handle_hotkey(browser, warn_lost_event, title, sizeof(title), key))
766                         break;
767         }
768 out:
769         ui_browser__hide(&browser->b);
770         return key;
771 }
772
773 struct callchain_print_arg {
774         /* for hists browser */
775         off_t   row_offset;
776         bool    is_current_entry;
777
778         /* for file dump */
779         FILE    *fp;
780         int     printed;
781 };
782
783 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
784                                          struct callchain_list *chain,
785                                          const char *str, int offset,
786                                          unsigned short row,
787                                          struct callchain_print_arg *arg);
788
789 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
790                                                struct callchain_list *chain,
791                                                const char *str, int offset,
792                                                unsigned short row,
793                                                struct callchain_print_arg *arg)
794 {
795         int color, width;
796         char folded_sign = callchain_list__folded(chain);
797         bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
798
799         color = HE_COLORSET_NORMAL;
800         width = browser->b.width - (offset + 2);
801         if (ui_browser__is_current_entry(&browser->b, row)) {
802                 browser->selection = &chain->ms;
803                 color = HE_COLORSET_SELECTED;
804                 arg->is_current_entry = true;
805         }
806
807         ui_browser__set_color(&browser->b, color);
808         ui_browser__gotorc(&browser->b, row, 0);
809         ui_browser__write_nstring(&browser->b, " ", offset);
810         ui_browser__printf(&browser->b, "%c", folded_sign);
811         ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
812         ui_browser__write_nstring(&browser->b, str, width);
813 }
814
815 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
816                                                   struct callchain_list *chain,
817                                                   const char *str, int offset,
818                                                   unsigned short row __maybe_unused,
819                                                   struct callchain_print_arg *arg)
820 {
821         char folded_sign = callchain_list__folded(chain);
822
823         arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
824                                 folded_sign, str);
825 }
826
827 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
828                                      unsigned short row);
829
830 static bool hist_browser__check_output_full(struct hist_browser *browser,
831                                             unsigned short row)
832 {
833         return browser->b.rows == row;
834 }
835
836 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
837                                           unsigned short row __maybe_unused)
838 {
839         return false;
840 }
841
842 #define LEVEL_OFFSET_STEP 3
843
844 static int hist_browser__show_callchain_list(struct hist_browser *browser,
845                                              struct callchain_node *node,
846                                              struct callchain_list *chain,
847                                              unsigned short row, u64 total,
848                                              bool need_percent, int offset,
849                                              print_callchain_entry_fn print,
850                                              struct callchain_print_arg *arg)
851 {
852         char bf[1024], *alloc_str;
853         char buf[64], *alloc_str2;
854         const char *str;
855         int ret = 1;
856
857         if (arg->row_offset != 0) {
858                 arg->row_offset--;
859                 return 0;
860         }
861
862         alloc_str = NULL;
863         alloc_str2 = NULL;
864
865         str = callchain_list__sym_name(chain, bf, sizeof(bf),
866                                        browser->show_dso);
867
868         if (symbol_conf.show_branchflag_count) {
869                 callchain_list_counts__printf_value(chain, NULL,
870                                                     buf, sizeof(buf));
871
872                 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
873                         str = "Not enough memory!";
874                 else
875                         str = alloc_str2;
876         }
877
878         if (need_percent) {
879                 callchain_node__scnprintf_value(node, buf, sizeof(buf),
880                                                 total);
881
882                 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
883                         str = "Not enough memory!";
884                 else
885                         str = alloc_str;
886         }
887
888         print(browser, chain, str, offset, row, arg);
889         free(alloc_str);
890         free(alloc_str2);
891
892         return ret;
893 }
894
895 static bool check_percent_display(struct rb_node *node, u64 parent_total)
896 {
897         struct callchain_node *child;
898
899         if (node == NULL)
900                 return false;
901
902         if (rb_next(node))
903                 return true;
904
905         child = rb_entry(node, struct callchain_node, rb_node);
906         return callchain_cumul_hits(child) != parent_total;
907 }
908
909 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
910                                              struct rb_root *root,
911                                              unsigned short row, u64 total,
912                                              u64 parent_total,
913                                              print_callchain_entry_fn print,
914                                              struct callchain_print_arg *arg,
915                                              check_output_full_fn is_output_full)
916 {
917         struct rb_node *node;
918         int first_row = row, offset = LEVEL_OFFSET_STEP;
919         bool need_percent;
920
921         node = rb_first(root);
922         need_percent = check_percent_display(node, parent_total);
923
924         while (node) {
925                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
926                 struct rb_node *next = rb_next(node);
927                 struct callchain_list *chain;
928                 char folded_sign = ' ';
929                 int first = true;
930                 int extra_offset = 0;
931
932                 list_for_each_entry(chain, &child->parent_val, list) {
933                         bool was_first = first;
934
935                         if (first)
936                                 first = false;
937                         else if (need_percent)
938                                 extra_offset = LEVEL_OFFSET_STEP;
939
940                         folded_sign = callchain_list__folded(chain);
941
942                         row += hist_browser__show_callchain_list(browser, child,
943                                                         chain, row, total,
944                                                         was_first && need_percent,
945                                                         offset + extra_offset,
946                                                         print, arg);
947
948                         if (is_output_full(browser, row))
949                                 goto out;
950
951                         if (folded_sign == '+')
952                                 goto next;
953                 }
954
955                 list_for_each_entry(chain, &child->val, list) {
956                         bool was_first = first;
957
958                         if (first)
959                                 first = false;
960                         else if (need_percent)
961                                 extra_offset = LEVEL_OFFSET_STEP;
962
963                         folded_sign = callchain_list__folded(chain);
964
965                         row += hist_browser__show_callchain_list(browser, child,
966                                                         chain, row, total,
967                                                         was_first && need_percent,
968                                                         offset + extra_offset,
969                                                         print, arg);
970
971                         if (is_output_full(browser, row))
972                                 goto out;
973
974                         if (folded_sign == '+')
975                                 break;
976                 }
977
978 next:
979                 if (is_output_full(browser, row))
980                         break;
981                 node = next;
982         }
983 out:
984         return row - first_row;
985 }
986
987 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
988                                                 struct callchain_list *chain,
989                                                 char *value_str, char *old_str)
990 {
991         char bf[1024];
992         const char *str;
993         char *new;
994
995         str = callchain_list__sym_name(chain, bf, sizeof(bf),
996                                        browser->show_dso);
997         if (old_str) {
998                 if (asprintf(&new, "%s%s%s", old_str,
999                              symbol_conf.field_sep ?: ";", str) < 0)
1000                         new = NULL;
1001         } else {
1002                 if (value_str) {
1003                         if (asprintf(&new, "%s %s", value_str, str) < 0)
1004                                 new = NULL;
1005                 } else {
1006                         if (asprintf(&new, "%s", str) < 0)
1007                                 new = NULL;
1008                 }
1009         }
1010         return new;
1011 }
1012
1013 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1014                                                struct rb_root *root,
1015                                                unsigned short row, u64 total,
1016                                                u64 parent_total,
1017                                                print_callchain_entry_fn print,
1018                                                struct callchain_print_arg *arg,
1019                                                check_output_full_fn is_output_full)
1020 {
1021         struct rb_node *node;
1022         int first_row = row, offset = LEVEL_OFFSET_STEP;
1023         bool need_percent;
1024
1025         node = rb_first(root);
1026         need_percent = check_percent_display(node, parent_total);
1027
1028         while (node) {
1029                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1030                 struct rb_node *next = rb_next(node);
1031                 struct callchain_list *chain, *first_chain = NULL;
1032                 int first = true;
1033                 char *value_str = NULL, *value_str_alloc = NULL;
1034                 char *chain_str = NULL, *chain_str_alloc = NULL;
1035
1036                 if (arg->row_offset != 0) {
1037                         arg->row_offset--;
1038                         goto next;
1039                 }
1040
1041                 if (need_percent) {
1042                         char buf[64];
1043
1044                         callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1045                         if (asprintf(&value_str, "%s", buf) < 0) {
1046                                 value_str = (char *)"<...>";
1047                                 goto do_print;
1048                         }
1049                         value_str_alloc = value_str;
1050                 }
1051
1052                 list_for_each_entry(chain, &child->parent_val, list) {
1053                         chain_str = hist_browser__folded_callchain_str(browser,
1054                                                 chain, value_str, chain_str);
1055                         if (first) {
1056                                 first = false;
1057                                 first_chain = chain;
1058                         }
1059
1060                         if (chain_str == NULL) {
1061                                 chain_str = (char *)"Not enough memory!";
1062                                 goto do_print;
1063                         }
1064
1065                         chain_str_alloc = chain_str;
1066                 }
1067
1068                 list_for_each_entry(chain, &child->val, list) {
1069                         chain_str = hist_browser__folded_callchain_str(browser,
1070                                                 chain, value_str, chain_str);
1071                         if (first) {
1072                                 first = false;
1073                                 first_chain = chain;
1074                         }
1075
1076                         if (chain_str == NULL) {
1077                                 chain_str = (char *)"Not enough memory!";
1078                                 goto do_print;
1079                         }
1080
1081                         chain_str_alloc = chain_str;
1082                 }
1083
1084 do_print:
1085                 print(browser, first_chain, chain_str, offset, row++, arg);
1086                 free(value_str_alloc);
1087                 free(chain_str_alloc);
1088
1089 next:
1090                 if (is_output_full(browser, row))
1091                         break;
1092                 node = next;
1093         }
1094
1095         return row - first_row;
1096 }
1097
1098 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1099                                         struct rb_root *root, int level,
1100                                         unsigned short row, u64 total,
1101                                         u64 parent_total,
1102                                         print_callchain_entry_fn print,
1103                                         struct callchain_print_arg *arg,
1104                                         check_output_full_fn is_output_full)
1105 {
1106         struct rb_node *node;
1107         int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1108         bool need_percent;
1109         u64 percent_total = total;
1110
1111         if (callchain_param.mode == CHAIN_GRAPH_REL)
1112                 percent_total = parent_total;
1113
1114         node = rb_first(root);
1115         need_percent = check_percent_display(node, parent_total);
1116
1117         while (node) {
1118                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1119                 struct rb_node *next = rb_next(node);
1120                 struct callchain_list *chain;
1121                 char folded_sign = ' ';
1122                 int first = true;
1123                 int extra_offset = 0;
1124
1125                 list_for_each_entry(chain, &child->val, list) {
1126                         bool was_first = first;
1127
1128                         if (first)
1129                                 first = false;
1130                         else if (need_percent)
1131                                 extra_offset = LEVEL_OFFSET_STEP;
1132
1133                         folded_sign = callchain_list__folded(chain);
1134
1135                         row += hist_browser__show_callchain_list(browser, child,
1136                                                         chain, row, percent_total,
1137                                                         was_first && need_percent,
1138                                                         offset + extra_offset,
1139                                                         print, arg);
1140
1141                         if (is_output_full(browser, row))
1142                                 goto out;
1143
1144                         if (folded_sign == '+')
1145                                 break;
1146                 }
1147
1148                 if (folded_sign == '-') {
1149                         const int new_level = level + (extra_offset ? 2 : 1);
1150
1151                         row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1152                                                             new_level, row, total,
1153                                                             child->children_hit,
1154                                                             print, arg, is_output_full);
1155                 }
1156                 if (is_output_full(browser, row))
1157                         break;
1158                 node = next;
1159         }
1160 out:
1161         return row - first_row;
1162 }
1163
1164 static int hist_browser__show_callchain(struct hist_browser *browser,
1165                                         struct hist_entry *entry, int level,
1166                                         unsigned short row,
1167                                         print_callchain_entry_fn print,
1168                                         struct callchain_print_arg *arg,
1169                                         check_output_full_fn is_output_full)
1170 {
1171         u64 total = hists__total_period(entry->hists);
1172         u64 parent_total;
1173         int printed;
1174
1175         if (symbol_conf.cumulate_callchain)
1176                 parent_total = entry->stat_acc->period;
1177         else
1178                 parent_total = entry->stat.period;
1179
1180         if (callchain_param.mode == CHAIN_FLAT) {
1181                 printed = hist_browser__show_callchain_flat(browser,
1182                                                 &entry->sorted_chain, row,
1183                                                 total, parent_total, print, arg,
1184                                                 is_output_full);
1185         } else if (callchain_param.mode == CHAIN_FOLDED) {
1186                 printed = hist_browser__show_callchain_folded(browser,
1187                                                 &entry->sorted_chain, row,
1188                                                 total, parent_total, print, arg,
1189                                                 is_output_full);
1190         } else {
1191                 printed = hist_browser__show_callchain_graph(browser,
1192                                                 &entry->sorted_chain, level, row,
1193                                                 total, parent_total, print, arg,
1194                                                 is_output_full);
1195         }
1196
1197         if (arg->is_current_entry)
1198                 browser->he_selection = entry;
1199
1200         return printed;
1201 }
1202
1203 struct hpp_arg {
1204         struct ui_browser *b;
1205         char folded_sign;
1206         bool current_entry;
1207 };
1208
1209 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1210 {
1211         struct hpp_arg *arg = hpp->ptr;
1212         int ret, len;
1213         va_list args;
1214         double percent;
1215
1216         va_start(args, fmt);
1217         len = va_arg(args, int);
1218         percent = va_arg(args, double);
1219         va_end(args);
1220
1221         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1222
1223         ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1224         ui_browser__printf(arg->b, "%s", hpp->buf);
1225
1226         return ret;
1227 }
1228
1229 #define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype)                 \
1230 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
1231 {                                                                       \
1232         return he->stat._field;                                         \
1233 }                                                                       \
1234                                                                         \
1235 static int                                                              \
1236 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1237                                 struct perf_hpp *hpp,                   \
1238                                 struct hist_entry *he)                  \
1239 {                                                                       \
1240         return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
1241                         __hpp__slsmg_color_printf, _fmttype);           \
1242 }
1243
1244 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype)             \
1245 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
1246 {                                                                       \
1247         return he->stat_acc->_field;                                    \
1248 }                                                                       \
1249                                                                         \
1250 static int                                                              \
1251 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
1252                                 struct perf_hpp *hpp,                   \
1253                                 struct hist_entry *he)                  \
1254 {                                                                       \
1255         if (!symbol_conf.cumulate_callchain) {                          \
1256                 struct hpp_arg *arg = hpp->ptr;                         \
1257                 int len = fmt->user_len ?: fmt->len;                    \
1258                 int ret = scnprintf(hpp->buf, hpp->size,                \
1259                                     "%*s", len, "N/A");                 \
1260                 ui_browser__printf(arg->b, "%s", hpp->buf);             \
1261                                                                         \
1262                 return ret;                                             \
1263         }                                                               \
1264         return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
1265                         " %*.2f%%", __hpp__slsmg_color_printf,          \
1266                         _fmttype);                                      \
1267 }
1268
1269 #define __HPP_COLOR_MEM_STAT_FN(_name, _type)                           \
1270 static int                                                              \
1271 hist_browser__hpp_color_mem_stat_##_name(struct perf_hpp_fmt *fmt,      \
1272                                          struct perf_hpp *hpp,          \
1273                                          struct hist_entry *he)         \
1274 {                                                                       \
1275         return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type,   \
1276                                  " %5.1f%%", __hpp__slsmg_color_printf);\
1277 }
1278
1279 __HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT)
1280 __HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY)
1281 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT)
1282 __HPP_COLOR_PERCENT_FN(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT)
1283 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT)
1284 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT)
1285 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT)
1286 __HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY)
1287 __HPP_COLOR_MEM_STAT_FN(op, OP)
1288 __HPP_COLOR_MEM_STAT_FN(cache, CACHE)
1289 __HPP_COLOR_MEM_STAT_FN(memory, MEMORY)
1290 __HPP_COLOR_MEM_STAT_FN(snoop, SNOOP)
1291 __HPP_COLOR_MEM_STAT_FN(dtlb, DTLB)
1292
1293 #undef __HPP_COLOR_PERCENT_FN
1294 #undef __HPP_COLOR_ACC_PERCENT_FN
1295 #undef __HPP_COLOR_MEM_STAT_FN
1296
1297 void hist_browser__init_hpp(void)
1298 {
1299         perf_hpp__format[PERF_HPP__OVERHEAD].color =
1300                                 hist_browser__hpp_color_overhead;
1301         perf_hpp__format[PERF_HPP__LATENCY].color =
1302                                 hist_browser__hpp_color_latency;
1303         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1304                                 hist_browser__hpp_color_overhead_sys;
1305         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1306                                 hist_browser__hpp_color_overhead_us;
1307         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1308                                 hist_browser__hpp_color_overhead_guest_sys;
1309         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1310                                 hist_browser__hpp_color_overhead_guest_us;
1311         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1312                                 hist_browser__hpp_color_overhead_acc;
1313         perf_hpp__format[PERF_HPP__LATENCY_ACC].color =
1314                                 hist_browser__hpp_color_latency_acc;
1315         perf_hpp__format[PERF_HPP__MEM_STAT_OP].color =
1316                                 hist_browser__hpp_color_mem_stat_op;
1317         perf_hpp__format[PERF_HPP__MEM_STAT_CACHE].color =
1318                                 hist_browser__hpp_color_mem_stat_cache;
1319         perf_hpp__format[PERF_HPP__MEM_STAT_MEMORY].color =
1320                                 hist_browser__hpp_color_mem_stat_memory;
1321         perf_hpp__format[PERF_HPP__MEM_STAT_SNOOP].color =
1322                                 hist_browser__hpp_color_mem_stat_snoop;
1323         perf_hpp__format[PERF_HPP__MEM_STAT_DTLB].color =
1324                                 hist_browser__hpp_color_mem_stat_dtlb;
1325
1326         res_sample_init();
1327 }
1328
1329 static int hist_browser__show_entry(struct hist_browser *browser,
1330                                     struct hist_entry *entry,
1331                                     unsigned short row)
1332 {
1333         int printed = 0;
1334         int width = browser->b.width;
1335         char folded_sign = ' ';
1336         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1337         bool use_callchain = hist_entry__has_callchains(entry) && symbol_conf.use_callchain;
1338         off_t row_offset = entry->row_offset;
1339         bool first = true;
1340         struct perf_hpp_fmt *fmt;
1341
1342         if (current_entry) {
1343                 browser->he_selection = entry;
1344                 browser->selection = &entry->ms;
1345         }
1346
1347         if (use_callchain) {
1348                 hist_entry__init_have_children(entry);
1349                 folded_sign = hist_entry__folded(entry);
1350         }
1351
1352         if (row_offset == 0) {
1353                 struct hpp_arg arg = {
1354                         .b              = &browser->b,
1355                         .folded_sign    = folded_sign,
1356                         .current_entry  = current_entry,
1357                 };
1358                 int column = 0;
1359
1360                 ui_browser__gotorc(&browser->b, row, 0);
1361
1362                 hists__for_each_format(browser->hists, fmt) {
1363                         char s[2048];
1364                         struct perf_hpp hpp = {
1365                                 .buf    = s,
1366                                 .size   = sizeof(s),
1367                                 .ptr    = &arg,
1368                         };
1369
1370                         if (perf_hpp__should_skip(fmt, entry->hists) ||
1371                             column++ < browser->b.horiz_scroll)
1372                                 continue;
1373
1374                         if (current_entry && browser->b.navkeypressed) {
1375                                 ui_browser__set_color(&browser->b,
1376                                                       HE_COLORSET_SELECTED);
1377                         } else {
1378                                 ui_browser__set_color(&browser->b,
1379                                                       HE_COLORSET_NORMAL);
1380                         }
1381
1382                         if (first) {
1383                                 if (use_callchain) {
1384                                         ui_browser__printf(&browser->b, "%c ", folded_sign);
1385                                         width -= 2;
1386                                 }
1387                                 first = false;
1388                         } else {
1389                                 ui_browser__printf(&browser->b, "  ");
1390                                 width -= 2;
1391                         }
1392
1393                         if (fmt->color) {
1394                                 int ret = fmt->color(fmt, &hpp, entry);
1395                                 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1396                                 /*
1397                                  * fmt->color() already used ui_browser to
1398                                  * print the non alignment bits, skip it (+ret):
1399                                  */
1400                                 ui_browser__printf(&browser->b, "%s", s + ret);
1401                         } else {
1402                                 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1403                                 ui_browser__printf(&browser->b, "%s", s);
1404                         }
1405                         width -= hpp.buf - s;
1406                 }
1407
1408                 /* The scroll bar isn't being used */
1409                 if (!browser->b.navkeypressed)
1410                         width += 1;
1411
1412                 ui_browser__write_nstring(&browser->b, "", width);
1413
1414                 ++row;
1415                 ++printed;
1416         } else
1417                 --row_offset;
1418
1419         if (folded_sign == '-' && row != browser->b.rows) {
1420                 struct callchain_print_arg arg = {
1421                         .row_offset = row_offset,
1422                         .is_current_entry = current_entry,
1423                 };
1424
1425                 printed += hist_browser__show_callchain(browser,
1426                                 entry, 1, row,
1427                                 hist_browser__show_callchain_entry,
1428                                 &arg,
1429                                 hist_browser__check_output_full);
1430         }
1431
1432         return printed;
1433 }
1434
1435 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1436                                               struct hist_entry *entry,
1437                                               unsigned short row,
1438                                               int level)
1439 {
1440         int printed = 0;
1441         int width = browser->b.width;
1442         char folded_sign = ' ';
1443         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1444         off_t row_offset = entry->row_offset;
1445         bool first = true;
1446         struct perf_hpp_fmt *fmt;
1447         struct perf_hpp_list_node *fmt_node;
1448         struct hpp_arg arg = {
1449                 .b              = &browser->b,
1450                 .current_entry  = current_entry,
1451         };
1452         int column = 0;
1453         int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1454
1455         if (current_entry) {
1456                 browser->he_selection = entry;
1457                 browser->selection = &entry->ms;
1458         }
1459
1460         hist_entry__init_have_children(entry);
1461         folded_sign = hist_entry__folded(entry);
1462         arg.folded_sign = folded_sign;
1463
1464         if (entry->leaf && row_offset) {
1465                 row_offset--;
1466                 goto show_callchain;
1467         }
1468
1469         ui_browser__gotorc(&browser->b, row, 0);
1470
1471         if (current_entry && browser->b.navkeypressed)
1472                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1473         else
1474                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1475
1476         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1477         width -= level * HIERARCHY_INDENT;
1478
1479         /* the first hpp_list_node is for overhead columns */
1480         fmt_node = list_first_entry(&entry->hists->hpp_formats,
1481                                     struct perf_hpp_list_node, list);
1482         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1483                 char s[2048];
1484                 struct perf_hpp hpp = {
1485                         .buf            = s,
1486                         .size           = sizeof(s),
1487                         .ptr            = &arg,
1488                 };
1489
1490                 if (perf_hpp__should_skip(fmt, entry->hists) ||
1491                     column++ < browser->b.horiz_scroll)
1492                         continue;
1493
1494                 if (current_entry && browser->b.navkeypressed) {
1495                         ui_browser__set_color(&browser->b,
1496                                               HE_COLORSET_SELECTED);
1497                 } else {
1498                         ui_browser__set_color(&browser->b,
1499                                               HE_COLORSET_NORMAL);
1500                 }
1501
1502                 if (first) {
1503                         ui_browser__printf(&browser->b, "%c ", folded_sign);
1504                         width -= 2;
1505                         first = false;
1506                 } else {
1507                         ui_browser__printf(&browser->b, "  ");
1508                         width -= 2;
1509                 }
1510
1511                 if (fmt->color) {
1512                         int ret = fmt->color(fmt, &hpp, entry);
1513                         hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1514                         /*
1515                          * fmt->color() already used ui_browser to
1516                          * print the non alignment bits, skip it (+ret):
1517                          */
1518                         ui_browser__printf(&browser->b, "%s", s + ret);
1519                 } else {
1520                         int ret = fmt->entry(fmt, &hpp, entry);
1521                         hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1522                         ui_browser__printf(&browser->b, "%s", s);
1523                 }
1524                 width -= hpp.buf - s;
1525         }
1526
1527         if (!first) {
1528                 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1529                 width -= hierarchy_indent;
1530         }
1531
1532         if (column >= browser->b.horiz_scroll) {
1533                 char s[2048];
1534                 struct perf_hpp hpp = {
1535                         .buf            = s,
1536                         .size           = sizeof(s),
1537                         .ptr            = &arg,
1538                 };
1539
1540                 if (current_entry && browser->b.navkeypressed) {
1541                         ui_browser__set_color(&browser->b,
1542                                               HE_COLORSET_SELECTED);
1543                 } else {
1544                         ui_browser__set_color(&browser->b,
1545                                               HE_COLORSET_NORMAL);
1546                 }
1547
1548                 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1549                         if (first) {
1550                                 ui_browser__printf(&browser->b, "%c ", folded_sign);
1551                                 first = false;
1552                         } else {
1553                                 ui_browser__write_nstring(&browser->b, "", 2);
1554                         }
1555
1556                         width -= 2;
1557
1558                         /*
1559                          * No need to call hist_entry__snprintf_alignment()
1560                          * since this fmt is always the last column in the
1561                          * hierarchy mode.
1562                          */
1563                         if (fmt->color) {
1564                                 width -= fmt->color(fmt, &hpp, entry);
1565                         } else {
1566                                 int i = 0;
1567
1568                                 width -= fmt->entry(fmt, &hpp, entry);
1569                                 ui_browser__printf(&browser->b, "%s", skip_spaces(s));
1570
1571                                 while (isspace(s[i++]))
1572                                         width++;
1573                         }
1574                 }
1575         }
1576
1577         /* The scroll bar isn't being used */
1578         if (!browser->b.navkeypressed)
1579                 width += 1;
1580
1581         ui_browser__write_nstring(&browser->b, "", width);
1582
1583         ++row;
1584         ++printed;
1585
1586 show_callchain:
1587         if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1588                 struct callchain_print_arg carg = {
1589                         .row_offset = row_offset,
1590                 };
1591
1592                 printed += hist_browser__show_callchain(browser, entry,
1593                                         level + 1, row,
1594                                         hist_browser__show_callchain_entry, &carg,
1595                                         hist_browser__check_output_full);
1596         }
1597
1598         return printed;
1599 }
1600
1601 static int hist_browser__show_no_entry(struct hist_browser *browser,
1602                                        unsigned short row, int level)
1603 {
1604         int width = browser->b.width;
1605         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1606         bool first = true;
1607         int column = 0;
1608         int ret;
1609         struct perf_hpp_fmt *fmt;
1610         struct perf_hpp_list_node *fmt_node;
1611         int indent = browser->hists->nr_hpp_node - 2;
1612
1613         if (current_entry) {
1614                 browser->he_selection = NULL;
1615                 browser->selection = NULL;
1616         }
1617
1618         ui_browser__gotorc(&browser->b, row, 0);
1619
1620         if (current_entry && browser->b.navkeypressed)
1621                 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1622         else
1623                 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1624
1625         ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1626         width -= level * HIERARCHY_INDENT;
1627
1628         /* the first hpp_list_node is for overhead columns */
1629         fmt_node = list_first_entry(&browser->hists->hpp_formats,
1630                                     struct perf_hpp_list_node, list);
1631         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1632                 if (perf_hpp__should_skip(fmt, browser->hists) ||
1633                     column++ < browser->b.horiz_scroll)
1634                         continue;
1635
1636                 ret = fmt->width(fmt, NULL, browser->hists);
1637
1638                 if (first) {
1639                         /* for folded sign */
1640                         first = false;
1641                         ret++;
1642                 } else {
1643                         /* space between columns */
1644                         ret += 2;
1645                 }
1646
1647                 ui_browser__write_nstring(&browser->b, "", ret);
1648                 width -= ret;
1649         }
1650
1651         ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1652         width -= indent * HIERARCHY_INDENT;
1653
1654         if (column >= browser->b.horiz_scroll) {
1655                 char buf[32];
1656
1657                 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1658                 ui_browser__printf(&browser->b, "  %s", buf);
1659                 width -= ret + 2;
1660         }
1661
1662         /* The scroll bar isn't being used */
1663         if (!browser->b.navkeypressed)
1664                 width += 1;
1665
1666         ui_browser__write_nstring(&browser->b, "", width);
1667         return 1;
1668 }
1669
1670 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1671 {
1672         advance_hpp(hpp, inc);
1673         return hpp->size <= 0;
1674 }
1675
1676 static int
1677 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1678                                  size_t size, int line)
1679 {
1680         struct hists *hists = browser->hists;
1681         struct perf_hpp dummy_hpp = {
1682                 .buf    = buf,
1683                 .size   = size,
1684         };
1685         struct perf_hpp_fmt *fmt;
1686         size_t ret = 0;
1687         int column = 0;
1688         int span = 0;
1689
1690         if (hists__has_callchains(hists) && symbol_conf.use_callchain) {
1691                 ret = scnprintf(buf, size, "  ");
1692                 if (advance_hpp_check(&dummy_hpp, ret))
1693                         return ret;
1694         }
1695
1696         hists__for_each_format(browser->hists, fmt) {
1697                 if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1698                         continue;
1699
1700                 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1701                 if (advance_hpp_check(&dummy_hpp, ret))
1702                         break;
1703
1704                 if (span)
1705                         continue;
1706
1707                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1708                 if (advance_hpp_check(&dummy_hpp, ret))
1709                         break;
1710         }
1711
1712         return ret;
1713 }
1714
1715 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser,
1716                                                       char *buf, size_t size, int line)
1717 {
1718         struct hists *hists = browser->hists;
1719         struct perf_hpp dummy_hpp = {
1720                 .buf    = buf,
1721                 .size   = size,
1722         };
1723         struct perf_hpp_fmt *fmt;
1724         struct perf_hpp_list_node *fmt_node;
1725         size_t ret = 0;
1726         int column = 0;
1727         int indent = hists->nr_hpp_node - 2;
1728         bool first_node, first_col;
1729
1730         ret = scnprintf(buf, size, "  ");
1731         if (advance_hpp_check(&dummy_hpp, ret))
1732                 return ret;
1733
1734         first_node = true;
1735         /* the first hpp_list_node is for overhead columns */
1736         fmt_node = list_first_entry(&hists->hpp_formats,
1737                                     struct perf_hpp_list_node, list);
1738         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1739                 if (column++ < browser->b.horiz_scroll)
1740                         continue;
1741
1742                 ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
1743                 if (advance_hpp_check(&dummy_hpp, ret))
1744                         break;
1745
1746                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1747                 if (advance_hpp_check(&dummy_hpp, ret))
1748                         break;
1749
1750                 first_node = false;
1751         }
1752
1753         if (line < hists->hpp_list->nr_header_lines - 1)
1754                 return ret;
1755
1756         if (!first_node) {
1757                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1758                                 indent * HIERARCHY_INDENT, "");
1759                 if (advance_hpp_check(&dummy_hpp, ret))
1760                         return ret;
1761         }
1762
1763         first_node = true;
1764         list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1765                 if (!first_node) {
1766                         ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1767                         if (advance_hpp_check(&dummy_hpp, ret))
1768                                 break;
1769                 }
1770                 first_node = false;
1771
1772                 first_col = true;
1773                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1774                         char *start;
1775
1776                         if (perf_hpp__should_skip(fmt, hists))
1777                                 continue;
1778
1779                         if (!first_col) {
1780                                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1781                                 if (advance_hpp_check(&dummy_hpp, ret))
1782                                         break;
1783                         }
1784                         first_col = false;
1785
1786                         ret = fmt->header(fmt, &dummy_hpp, hists, line, NULL);
1787                         dummy_hpp.buf[ret] = '\0';
1788
1789                         start = strim(dummy_hpp.buf);
1790                         ret = strlen(start);
1791
1792                         if (start != dummy_hpp.buf)
1793                                 memmove(dummy_hpp.buf, start, ret + 1);
1794
1795                         if (advance_hpp_check(&dummy_hpp, ret))
1796                                 break;
1797                 }
1798         }
1799
1800         return ret;
1801 }
1802
1803 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1804 {
1805         struct perf_hpp_list *hpp_list = browser->hists->hpp_list;
1806         char headers[1024];
1807         int line;
1808
1809         for (line = 0; line < hpp_list->nr_header_lines; line++) {
1810                 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1811                                                            sizeof(headers), line);
1812
1813                 ui_browser__gotorc_title(&browser->b, line, 0);
1814                 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1815                 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1816         }
1817 }
1818
1819 static void hists_browser__headers(struct hist_browser *browser)
1820 {
1821         struct hists *hists = browser->hists;
1822         struct perf_hpp_list *hpp_list = hists->hpp_list;
1823
1824         int line;
1825
1826         for (line = 0; line < hpp_list->nr_header_lines; line++) {
1827                 char headers[1024];
1828
1829                 hists_browser__scnprintf_headers(browser, headers,
1830                                                  sizeof(headers), line);
1831
1832                 ui_browser__gotorc_title(&browser->b, line, 0);
1833                 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1834                 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1835         }
1836 }
1837
1838 static void hist_browser__show_headers(struct hist_browser *browser)
1839 {
1840         if (symbol_conf.report_hierarchy)
1841                 hists_browser__hierarchy_headers(browser);
1842         else
1843                 hists_browser__headers(browser);
1844 }
1845
1846 static void ui_browser__hists_init_top(struct ui_browser *browser)
1847 {
1848         if (browser->top == NULL) {
1849                 struct hist_browser *hb;
1850
1851                 hb = container_of(browser, struct hist_browser, b);
1852                 browser->top = rb_first_cached(&hb->hists->entries);
1853         }
1854 }
1855
1856 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1857 {
1858         unsigned row = 0;
1859         struct rb_node *nd;
1860         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1861
1862         if (hb->show_headers)
1863                 hist_browser__show_headers(hb);
1864
1865         ui_browser__hists_init_top(browser);
1866         hb->he_selection = NULL;
1867         hb->selection = NULL;
1868
1869         for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1870                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1871                 float percent;
1872
1873                 if (h->filtered) {
1874                         /* let it move to sibling */
1875                         h->unfolded = false;
1876                         continue;
1877                 }
1878
1879                 if (symbol_conf.report_individual_block)
1880                         percent = block_info__total_cycles_percent(h);
1881                 else
1882                         percent = hist_entry__get_percent_limit(h);
1883
1884                 if (percent < hb->min_pcnt)
1885                         continue;
1886
1887                 if (symbol_conf.report_hierarchy) {
1888                         row += hist_browser__show_hierarchy_entry(hb, h, row,
1889                                                                   h->depth);
1890                         if (row == browser->rows)
1891                                 break;
1892
1893                         if (h->has_no_entry) {
1894                                 hist_browser__show_no_entry(hb, row, h->depth + 1);
1895                                 row++;
1896                         }
1897                 } else {
1898                         row += hist_browser__show_entry(hb, h, row);
1899                 }
1900
1901                 if (row == browser->rows)
1902                         break;
1903         }
1904
1905         return row;
1906 }
1907
1908 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1909                                              float min_pcnt)
1910 {
1911         while (nd != NULL) {
1912                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1913                 float percent = hist_entry__get_percent_limit(h);
1914
1915                 if (!h->filtered && percent >= min_pcnt)
1916                         return nd;
1917
1918                 /*
1919                  * If it's filtered, its all children also were filtered.
1920                  * So move to sibling node.
1921                  */
1922                 if (rb_next(nd))
1923                         nd = rb_next(nd);
1924                 else
1925                         nd = rb_hierarchy_next(nd);
1926         }
1927
1928         return NULL;
1929 }
1930
1931 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1932                                                   float min_pcnt)
1933 {
1934         while (nd != NULL) {
1935                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1936                 float percent = hist_entry__get_percent_limit(h);
1937
1938                 if (!h->filtered && percent >= min_pcnt)
1939                         return nd;
1940
1941                 nd = rb_hierarchy_prev(nd);
1942         }
1943
1944         return NULL;
1945 }
1946
1947 static void ui_browser__hists_seek(struct ui_browser *browser,
1948                                    off_t offset, int whence)
1949 {
1950         struct hist_entry *h;
1951         struct rb_node *nd;
1952         bool first = true;
1953         struct hist_browser *hb;
1954
1955         hb = container_of(browser, struct hist_browser, b);
1956
1957         if (browser->nr_entries == 0)
1958                 return;
1959
1960         ui_browser__hists_init_top(browser);
1961
1962         switch (whence) {
1963         case SEEK_SET:
1964                 nd = hists__filter_entries(rb_first(browser->entries),
1965                                            hb->min_pcnt);
1966                 break;
1967         case SEEK_CUR:
1968                 nd = browser->top;
1969                 goto do_offset;
1970         case SEEK_END:
1971                 nd = rb_hierarchy_last(rb_last(browser->entries));
1972                 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1973                 first = false;
1974                 break;
1975         default:
1976                 return;
1977         }
1978
1979         /*
1980          * Moves not relative to the first visible entry invalidates its
1981          * row_offset:
1982          */
1983         h = rb_entry(browser->top, struct hist_entry, rb_node);
1984         h->row_offset = 0;
1985
1986         /*
1987          * Here we have to check if nd is expanded (+), if it is we can't go
1988          * the next top level hist_entry, instead we must compute an offset of
1989          * what _not_ to show and not change the first visible entry.
1990          *
1991          * This offset increments when we are going from top to bottom and
1992          * decreases when we're going from bottom to top.
1993          *
1994          * As we don't have backpointers to the top level in the callchains
1995          * structure, we need to always print the whole hist_entry callchain,
1996          * skipping the first ones that are before the first visible entry
1997          * and stop when we printed enough lines to fill the screen.
1998          */
1999 do_offset:
2000         if (!nd)
2001                 return;
2002
2003         if (offset > 0) {
2004                 do {
2005                         h = rb_entry(nd, struct hist_entry, rb_node);
2006                         if (h->unfolded && h->leaf) {
2007                                 u16 remaining = h->nr_rows - h->row_offset;
2008                                 if (offset > remaining) {
2009                                         offset -= remaining;
2010                                         h->row_offset = 0;
2011                                 } else {
2012                                         h->row_offset += offset;
2013                                         offset = 0;
2014                                         browser->top = nd;
2015                                         break;
2016                                 }
2017                         }
2018                         nd = hists__filter_entries(rb_hierarchy_next(nd),
2019                                                    hb->min_pcnt);
2020                         if (nd == NULL)
2021                                 break;
2022                         --offset;
2023                         browser->top = nd;
2024                 } while (offset != 0);
2025         } else if (offset < 0) {
2026                 while (1) {
2027                         h = rb_entry(nd, struct hist_entry, rb_node);
2028                         if (h->unfolded && h->leaf) {
2029                                 if (first) {
2030                                         if (-offset > h->row_offset) {
2031                                                 offset += h->row_offset;
2032                                                 h->row_offset = 0;
2033                                         } else {
2034                                                 h->row_offset += offset;
2035                                                 offset = 0;
2036                                                 browser->top = nd;
2037                                                 break;
2038                                         }
2039                                 } else {
2040                                         if (-offset > h->nr_rows) {
2041                                                 offset += h->nr_rows;
2042                                                 h->row_offset = 0;
2043                                         } else {
2044                                                 h->row_offset = h->nr_rows + offset;
2045                                                 offset = 0;
2046                                                 browser->top = nd;
2047                                                 break;
2048                                         }
2049                                 }
2050                         }
2051
2052                         nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2053                                                         hb->min_pcnt);
2054                         if (nd == NULL)
2055                                 break;
2056                         ++offset;
2057                         browser->top = nd;
2058                         if (offset == 0) {
2059                                 /*
2060                                  * Last unfiltered hist_entry, check if it is
2061                                  * unfolded, if it is then we should have
2062                                  * row_offset at its last entry.
2063                                  */
2064                                 h = rb_entry(nd, struct hist_entry, rb_node);
2065                                 if (h->unfolded && h->leaf)
2066                                         h->row_offset = h->nr_rows;
2067                                 break;
2068                         }
2069                         first = false;
2070                 }
2071         } else {
2072                 browser->top = nd;
2073                 h = rb_entry(nd, struct hist_entry, rb_node);
2074                 h->row_offset = 0;
2075         }
2076 }
2077
2078 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2079                                            struct hist_entry *he, FILE *fp,
2080                                            int level)
2081 {
2082         struct callchain_print_arg arg  = {
2083                 .fp = fp,
2084         };
2085
2086         hist_browser__show_callchain(browser, he, level, 0,
2087                                      hist_browser__fprintf_callchain_entry, &arg,
2088                                      hist_browser__check_dump_full);
2089         return arg.printed;
2090 }
2091
2092 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2093                                        struct hist_entry *he, FILE *fp)
2094 {
2095         char s[8192];
2096         int printed = 0;
2097         char folded_sign = ' ';
2098         struct perf_hpp hpp = {
2099                 .buf = s,
2100                 .size = sizeof(s),
2101         };
2102         struct perf_hpp_fmt *fmt;
2103         bool first = true;
2104         int ret;
2105
2106         if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) {
2107                 folded_sign = hist_entry__folded(he);
2108                 printed += fprintf(fp, "%c ", folded_sign);
2109         }
2110
2111         hists__for_each_format(browser->hists, fmt) {
2112                 if (perf_hpp__should_skip(fmt, he->hists))
2113                         continue;
2114
2115                 if (!first) {
2116                         ret = scnprintf(hpp.buf, hpp.size, "  ");
2117                         advance_hpp(&hpp, ret);
2118                 } else
2119                         first = false;
2120
2121                 ret = fmt->entry(fmt, &hpp, he);
2122                 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2123                 advance_hpp(&hpp, ret);
2124         }
2125         printed += fprintf(fp, "%s\n", s);
2126
2127         if (folded_sign == '-')
2128                 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2129
2130         return printed;
2131 }
2132
2133
2134 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2135                                                  struct hist_entry *he,
2136                                                  FILE *fp, int level)
2137 {
2138         char s[8192];
2139         int printed = 0;
2140         char folded_sign = ' ';
2141         struct perf_hpp hpp = {
2142                 .buf = s,
2143                 .size = sizeof(s),
2144         };
2145         struct perf_hpp_fmt *fmt;
2146         struct perf_hpp_list_node *fmt_node;
2147         bool first = true;
2148         int ret;
2149         int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2150
2151         printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2152
2153         folded_sign = hist_entry__folded(he);
2154         printed += fprintf(fp, "%c", folded_sign);
2155
2156         /* the first hpp_list_node is for overhead columns */
2157         fmt_node = list_first_entry(&he->hists->hpp_formats,
2158                                     struct perf_hpp_list_node, list);
2159         perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2160                 if (!first) {
2161                         ret = scnprintf(hpp.buf, hpp.size, "  ");
2162                         advance_hpp(&hpp, ret);
2163                 } else
2164                         first = false;
2165
2166                 ret = fmt->entry(fmt, &hpp, he);
2167                 advance_hpp(&hpp, ret);
2168         }
2169
2170         ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2171         advance_hpp(&hpp, ret);
2172
2173         perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2174                 ret = scnprintf(hpp.buf, hpp.size, "  ");
2175                 advance_hpp(&hpp, ret);
2176
2177                 ret = fmt->entry(fmt, &hpp, he);
2178                 advance_hpp(&hpp, ret);
2179         }
2180
2181         strim(s);
2182         printed += fprintf(fp, "%s\n", s);
2183
2184         if (he->leaf && folded_sign == '-') {
2185                 printed += hist_browser__fprintf_callchain(browser, he, fp,
2186                                                            he->depth + 1);
2187         }
2188
2189         return printed;
2190 }
2191
2192 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2193 {
2194         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2195                                                    browser->min_pcnt);
2196         int printed = 0;
2197
2198         while (nd) {
2199                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2200
2201                 if (symbol_conf.report_hierarchy) {
2202                         printed += hist_browser__fprintf_hierarchy_entry(browser,
2203                                                                          h, fp,
2204                                                                          h->depth);
2205                 } else {
2206                         printed += hist_browser__fprintf_entry(browser, h, fp);
2207                 }
2208
2209                 nd = hists__filter_entries(rb_hierarchy_next(nd),
2210                                            browser->min_pcnt);
2211         }
2212
2213         return printed;
2214 }
2215
2216 static int hist_browser__dump(struct hist_browser *browser)
2217 {
2218         char filename[64];
2219         FILE *fp;
2220
2221         while (1) {
2222                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2223                 if (access(filename, F_OK))
2224                         break;
2225                 /*
2226                  * XXX: Just an arbitrary lazy upper limit
2227                  */
2228                 if (++browser->print_seq == 8192) {
2229                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2230                         return -1;
2231                 }
2232         }
2233
2234         fp = fopen(filename, "w");
2235         if (fp == NULL) {
2236                 char bf[64];
2237                 const char *err = str_error_r(errno, bf, sizeof(bf));
2238                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2239                 return -1;
2240         }
2241
2242         ++browser->print_seq;
2243         hist_browser__fprintf(browser, fp);
2244         fclose(fp);
2245         ui_helpline__fpush("%s written!", filename);
2246
2247         return 0;
2248 }
2249
2250 void hist_browser__init(struct hist_browser *browser,
2251                         struct hists *hists)
2252 {
2253         struct perf_hpp_fmt *fmt;
2254
2255         browser->hists                  = hists;
2256         browser->b.refresh              = hist_browser__refresh;
2257         browser->b.refresh_dimensions   = hist_browser__refresh_dimensions;
2258         browser->b.seek                 = ui_browser__hists_seek;
2259         browser->b.use_navkeypressed    = true;
2260         browser->show_headers           = symbol_conf.show_hist_headers;
2261         hist_browser__set_title_space(browser);
2262
2263         if (symbol_conf.report_hierarchy) {
2264                 struct perf_hpp_list_node *fmt_node;
2265
2266                 /* count overhead columns (in the first node) */
2267                 fmt_node = list_first_entry(&hists->hpp_formats,
2268                                             struct perf_hpp_list_node, list);
2269                 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2270                         ++browser->b.columns;
2271
2272                 /* add a single column for whole hierarchy sort keys*/
2273                 ++browser->b.columns;
2274         } else {
2275                 hists__for_each_format(hists, fmt)
2276                         ++browser->b.columns;
2277         }
2278
2279         hists__reset_column_width(hists);
2280 }
2281
2282 struct hist_browser *hist_browser__new(struct hists *hists)
2283 {
2284         struct hist_browser *browser = zalloc(sizeof(*browser));
2285
2286         if (browser)
2287                 hist_browser__init(browser, hists);
2288
2289         return browser;
2290 }
2291
2292 static struct hist_browser *
2293 perf_evsel_browser__new(struct evsel *evsel,
2294                         struct hist_browser_timer *hbt,
2295                         struct perf_env *env)
2296 {
2297         struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2298
2299         if (browser) {
2300                 browser->hbt   = hbt;
2301                 browser->env   = env;
2302                 browser->title = hists_browser__scnprintf_title;
2303         }
2304         return browser;
2305 }
2306
2307 void hist_browser__delete(struct hist_browser *browser)
2308 {
2309         free(browser);
2310 }
2311
2312 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2313 {
2314         return browser->he_selection;
2315 }
2316
2317 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2318 {
2319         return browser->he_selection->thread;
2320 }
2321
2322 static struct res_sample *hist_browser__selected_res_sample(struct hist_browser *browser)
2323 {
2324         return browser->he_selection ? browser->he_selection->res_samples : NULL;
2325 }
2326
2327 /* Check whether the browser is for 'top' or 'report' */
2328 static inline bool is_report_browser(void *timer)
2329 {
2330         return timer == NULL;
2331 }
2332
2333 static int hists_browser__scnprintf_title(struct hist_browser *browser, char *bf, size_t size)
2334 {
2335         struct hist_browser_timer *hbt = browser->hbt;
2336         int printed = __hists__scnprintf_title(browser->hists, bf, size, !is_report_browser(hbt));
2337
2338         if (!is_report_browser(hbt)) {
2339                 struct perf_top *top = hbt->arg;
2340
2341                 printed += scnprintf(bf + printed, size - printed,
2342                                      " lost: %" PRIu64 "/%" PRIu64,
2343                                      top->lost, top->lost_total);
2344
2345                 printed += scnprintf(bf + printed, size - printed,
2346                                      " drop: %" PRIu64 "/%" PRIu64,
2347                                      top->drop, top->drop_total);
2348
2349                 if (top->zero)
2350                         printed += scnprintf(bf + printed, size - printed, " [z]");
2351
2352                 perf_top__reset_sample_counters(top);
2353         }
2354
2355
2356         return printed;
2357 }
2358
2359 static inline void free_popup_options(char **options, int n)
2360 {
2361         int i;
2362
2363         for (i = 0; i < n; ++i)
2364                 zfree(&options[i]);
2365 }
2366
2367 /*
2368  * Only runtime switching of perf data file will make "input_name" point
2369  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2370  * whether we need to call free() for current "input_name" during the switch.
2371  */
2372 static bool is_input_name_malloced = false;
2373
2374 static int switch_data_file(void)
2375 {
2376         char *pwd, *options[32], *abs_path[32], *tmp;
2377         DIR *pwd_dir;
2378         int nr_options = 0, choice = -1, ret = -1;
2379         struct dirent *dent;
2380
2381         pwd = getenv("PWD");
2382         if (!pwd)
2383                 return ret;
2384
2385         pwd_dir = opendir(pwd);
2386         if (!pwd_dir)
2387                 return ret;
2388
2389         memset(options, 0, sizeof(options));
2390         memset(abs_path, 0, sizeof(abs_path));
2391
2392         while ((dent = readdir(pwd_dir))) {
2393                 char path[PATH_MAX];
2394                 u64 magic;
2395                 char *name = dent->d_name;
2396                 FILE *file;
2397
2398                 if (!(dent->d_type == DT_REG))
2399                         continue;
2400
2401                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2402
2403                 file = fopen(path, "r");
2404                 if (!file)
2405                         continue;
2406
2407                 if (fread(&magic, 1, 8, file) < 8)
2408                         goto close_file_and_continue;
2409
2410                 if (is_perf_magic(magic)) {
2411                         options[nr_options] = strdup(name);
2412                         if (!options[nr_options])
2413                                 goto close_file_and_continue;
2414
2415                         abs_path[nr_options] = strdup(path);
2416                         if (!abs_path[nr_options]) {
2417                                 zfree(&options[nr_options]);
2418                                 ui__warning("Can't search all data files due to memory shortage.\n");
2419                                 fclose(file);
2420                                 break;
2421                         }
2422
2423                         nr_options++;
2424                 }
2425
2426 close_file_and_continue:
2427                 fclose(file);
2428                 if (nr_options >= 32) {
2429                         ui__warning("Too many perf data files in PWD!\n"
2430                                     "Only the first 32 files will be listed.\n");
2431                         break;
2432                 }
2433         }
2434         closedir(pwd_dir);
2435
2436         if (nr_options) {
2437                 choice = ui__popup_menu(nr_options, options, NULL);
2438                 if (choice < nr_options && choice >= 0) {
2439                         tmp = strdup(abs_path[choice]);
2440                         if (tmp) {
2441                                 if (is_input_name_malloced)
2442                                         free((void *)input_name);
2443                                 input_name = tmp;
2444                                 is_input_name_malloced = true;
2445                                 ret = 0;
2446                         } else
2447                                 ui__warning("Data switch failed due to memory shortage!\n");
2448                 }
2449         }
2450
2451         free_popup_options(options, nr_options);
2452         free_popup_options(abs_path, nr_options);
2453         return ret;
2454 }
2455
2456 struct popup_action {
2457         unsigned long           time;
2458         struct thread           *thread;
2459         int (*fn)(struct hist_browser *browser, struct popup_action *act);
2460         struct map_symbol       ms;
2461         int                     socket;
2462         enum rstype             rstype;
2463
2464 };
2465
2466 static int
2467 do_annotate(struct hist_browser *browser, struct popup_action *act)
2468 {
2469         struct evsel *evsel;
2470         struct annotation *notes;
2471         struct hist_entry *he;
2472         int err;
2473
2474         if (!annotate_opts.objdump_path &&
2475             perf_env__lookup_objdump(browser->env, &annotate_opts.objdump_path))
2476                 return 0;
2477
2478         notes = symbol__annotation(act->ms.sym);
2479         if (!notes->src)
2480                 return 0;
2481
2482         if (browser->block_evsel)
2483                 evsel = browser->block_evsel;
2484         else
2485                 evsel = hists_to_evsel(browser->hists);
2486
2487         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2488         he = hist_browser__selected_entry(browser);
2489         /*
2490          * offer option to annotate the other branch source or target
2491          * (if they exists) when returning from annotate
2492          */
2493         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2494                 return 1;
2495
2496         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2497         if (err)
2498                 ui_browser__handle_resize(&browser->b);
2499         return 0;
2500 }
2501
2502 static struct symbol *symbol__new_unresolved(u64 addr, struct map *map)
2503 {
2504         struct annotated_source *src;
2505         struct symbol *sym;
2506         char name[64];
2507
2508         snprintf(name, sizeof(name), "%.*" PRIx64, BITS_PER_LONG / 4, addr);
2509
2510         sym = symbol__new(addr, ANNOTATION_DUMMY_LEN, 0, 0, name);
2511         if (sym) {
2512                 src = symbol__hists(sym, 1);
2513                 if (!src) {
2514                         symbol__delete(sym);
2515                         return NULL;
2516                 }
2517
2518                 dso__insert_symbol(map__dso(map), sym);
2519         }
2520
2521         return sym;
2522 }
2523
2524 static int
2525 add_annotate_opt(struct popup_action *act, char **optstr,
2526                  struct map_symbol *ms,
2527                  u64 addr)
2528 {
2529         struct dso *dso;
2530
2531         if (!ms->map || (dso = map__dso(ms->map)) == NULL || dso__annotate_warned(dso))
2532                 return 0;
2533
2534         if (!ms->sym)
2535                 ms->sym = symbol__new_unresolved(addr, ms->map);
2536
2537         if (ms->sym == NULL || symbol__annotation(ms->sym)->src == NULL)
2538                 return 0;
2539
2540         if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0)
2541                 return 0;
2542
2543         act->ms = *ms;
2544         act->fn = do_annotate;
2545         return 1;
2546 }
2547
2548 static int
2549 do_annotate_type(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2550 {
2551         struct hist_entry *he = browser->he_selection;
2552
2553         hist_entry__annotate_data_tui(he, hists_to_evsel(browser->hists), browser->hbt);
2554         ui_browser__handle_resize(&browser->b);
2555         return 0;
2556 }
2557
2558 static int
2559 add_annotate_type_opt(struct popup_action *act, char **optstr,
2560                       struct hist_entry *he)
2561 {
2562         if (he == NULL || he->mem_type == NULL || he->mem_type->histograms == NULL)
2563                 return 0;
2564
2565         if (asprintf(optstr, "Annotate type %s", he->mem_type->self.type_name) < 0)
2566                 return 0;
2567
2568         act->fn = do_annotate_type;
2569         return 1;
2570 }
2571
2572 static int
2573 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2574 {
2575         struct thread *thread = act->thread;
2576
2577         if ((!hists__has(browser->hists, thread) &&
2578              !hists__has(browser->hists, comm)) || thread == NULL)
2579                 return 0;
2580
2581         if (browser->hists->thread_filter) {
2582                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2583                 perf_hpp__set_elide(HISTC_THREAD, false);
2584                 thread__zput(browser->hists->thread_filter);
2585                 ui_helpline__pop();
2586         } else {
2587                 const char *comm_set_str =
2588                         thread__comm_set(thread) ? thread__comm_str(thread) : "";
2589
2590                 if (hists__has(browser->hists, thread)) {
2591                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2592                                            comm_set_str, thread__tid(thread));
2593                 } else {
2594                         ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2595                                            comm_set_str);
2596                 }
2597
2598                 browser->hists->thread_filter = thread__get(thread);
2599                 perf_hpp__set_elide(HISTC_THREAD, false);
2600                 pstack__push(browser->pstack, &browser->hists->thread_filter);
2601         }
2602
2603         hists__filter_by_thread(browser->hists);
2604         hist_browser__reset(browser);
2605         return 0;
2606 }
2607
2608 static int
2609 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2610                char **optstr, struct thread *thread)
2611 {
2612         int ret;
2613         const char *comm_set_str, *in_out;
2614
2615         if ((!hists__has(browser->hists, thread) &&
2616              !hists__has(browser->hists, comm)) || thread == NULL)
2617                 return 0;
2618
2619         in_out = browser->hists->thread_filter ? "out of" : "into";
2620         comm_set_str = thread__comm_set(thread) ? thread__comm_str(thread) : "";
2621         if (hists__has(browser->hists, thread)) {
2622                 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2623                                in_out, comm_set_str, thread__tid(thread));
2624         } else {
2625                 ret = asprintf(optstr, "Zoom %s %s thread", in_out, comm_set_str);
2626         }
2627         if (ret < 0)
2628                 return 0;
2629
2630         act->thread = thread;
2631         act->fn = do_zoom_thread;
2632         return 1;
2633 }
2634
2635 static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map)
2636 {
2637         if (!hists__has(browser->hists, dso) || map == NULL)
2638                 return 0;
2639
2640         if (browser->hists->dso_filter) {
2641                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2642                 perf_hpp__set_elide(HISTC_DSO, false);
2643                 browser->hists->dso_filter = NULL;
2644                 ui_helpline__pop();
2645         } else {
2646                 struct dso *dso = map__dso(map);
2647                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2648                                    __map__is_kernel(map) ? "the Kernel" : dso__short_name(dso));
2649                 browser->hists->dso_filter = dso;
2650                 perf_hpp__set_elide(HISTC_DSO, true);
2651                 pstack__push(browser->pstack, &browser->hists->dso_filter);
2652         }
2653
2654         hists__filter_by_dso(browser->hists);
2655         hist_browser__reset(browser);
2656         return 0;
2657 }
2658
2659 static int
2660 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2661 {
2662         return hists_browser__zoom_map(browser, act->ms.map);
2663 }
2664
2665 static int
2666 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2667             char **optstr, struct map *map)
2668 {
2669         if (!hists__has(browser->hists, dso) || map == NULL)
2670                 return 0;
2671
2672         if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)",
2673                      browser->hists->dso_filter ? "out of" : "into",
2674                      __map__is_kernel(map) ? "the Kernel" : dso__short_name(map__dso(map))) < 0)
2675                 return 0;
2676
2677         act->ms.map = map;
2678         act->fn = do_zoom_dso;
2679         return 1;
2680 }
2681
2682 static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused)
2683 {
2684         hist_browser__toggle_fold(browser);
2685         return 0;
2686 }
2687
2688 static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr)
2689 {
2690         char sym_name[512];
2691
2692         if (!hist_browser__selection_has_children(browser))
2693                 return 0;
2694
2695         if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)",
2696                      hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand",
2697                      hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0)
2698                 return 0;
2699
2700         act->fn = do_toggle_callchain;
2701         return 1;
2702 }
2703
2704 static int
2705 do_browse_map(struct hist_browser *browser __maybe_unused,
2706               struct popup_action *act)
2707 {
2708         map__browse(act->ms.map);
2709         return 0;
2710 }
2711
2712 static int
2713 add_map_opt(struct hist_browser *browser,
2714             struct popup_action *act, char **optstr, struct map *map)
2715 {
2716         if (!hists__has(browser->hists, dso) || map == NULL)
2717                 return 0;
2718
2719         if (asprintf(optstr, "Browse map details") < 0)
2720                 return 0;
2721
2722         act->ms.map = map;
2723         act->fn = do_browse_map;
2724         return 1;
2725 }
2726
2727 static int
2728 do_run_script(struct hist_browser *browser,
2729               struct popup_action *act)
2730 {
2731         char *script_opt;
2732         int len;
2733         int n = 0;
2734
2735         len = 100;
2736         if (act->thread)
2737                 len += strlen(thread__comm_str(act->thread));
2738         else if (act->ms.sym)
2739                 len += strlen(act->ms.sym->name);
2740         script_opt = malloc(len);
2741         if (!script_opt)
2742                 return -1;
2743
2744         script_opt[0] = 0;
2745         if (act->thread) {
2746                 n = scnprintf(script_opt, len, " -c %s ",
2747                           thread__comm_str(act->thread));
2748         } else if (act->ms.sym) {
2749                 n = scnprintf(script_opt, len, " -S %s ",
2750                           act->ms.sym->name);
2751         }
2752
2753         if (act->time) {
2754                 char start[32], end[32];
2755                 unsigned long starttime = act->time;
2756                 unsigned long endtime = act->time + symbol_conf.time_quantum;
2757
2758                 if (starttime == endtime) { /* Display 1ms as fallback */
2759                         starttime -= 1*NSEC_PER_MSEC;
2760                         endtime += 1*NSEC_PER_MSEC;
2761                 }
2762                 timestamp__scnprintf_usec(starttime, start, sizeof start);
2763                 timestamp__scnprintf_usec(endtime, end, sizeof end);
2764                 n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
2765         }
2766
2767         script_browse(script_opt, hists_to_evsel(browser->hists));
2768         free(script_opt);
2769         return 0;
2770 }
2771
2772 static int
2773 do_res_sample_script(struct hist_browser *browser,
2774                      struct popup_action *act)
2775 {
2776         struct hist_entry *he;
2777
2778         he = hist_browser__selected_entry(browser);
2779         res_sample_browse(he->res_samples, he->num_res, hists_to_evsel(browser->hists), act->rstype);
2780         return 0;
2781 }
2782
2783 static int
2784 add_script_opt_2(struct popup_action *act, char **optstr,
2785                struct thread *thread, struct symbol *sym,
2786                const char *tstr)
2787 {
2788
2789         if (thread) {
2790                 if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
2791                              thread__comm_str(thread), tstr) < 0)
2792                         return 0;
2793         } else if (sym) {
2794                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
2795                              sym->name, tstr) < 0)
2796                         return 0;
2797         } else {
2798                 if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
2799                         return 0;
2800         }
2801
2802         act->thread = thread;
2803         act->ms.sym = sym;
2804         act->fn = do_run_script;
2805         return 1;
2806 }
2807
2808 static int
2809 add_script_opt(struct hist_browser *browser,
2810                struct popup_action *act, char **optstr,
2811                struct thread *thread, struct symbol *sym)
2812 {
2813         int n, j;
2814         struct hist_entry *he;
2815
2816         n = add_script_opt_2(act, optstr, thread, sym, "");
2817
2818         he = hist_browser__selected_entry(browser);
2819         if (sort_order && strstr(sort_order, "time")) {
2820                 char tstr[128];
2821
2822                 optstr++;
2823                 act++;
2824                 j = sprintf(tstr, " in ");
2825                 j += timestamp__scnprintf_usec(he->time, tstr + j,
2826                                                sizeof tstr - j);
2827                 j += sprintf(tstr + j, "-");
2828                 timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
2829                                           tstr + j, sizeof tstr - j);
2830                 n += add_script_opt_2(act, optstr, thread, sym, tstr);
2831                 act->time = he->time;
2832         }
2833         return n;
2834 }
2835
2836 static int
2837 add_res_sample_opt(struct hist_browser *browser __maybe_unused,
2838                    struct popup_action *act, char **optstr,
2839                    struct res_sample *res_sample,
2840                    enum rstype type)
2841 {
2842         if (!res_sample)
2843                 return 0;
2844
2845         if (asprintf(optstr, "Show context for individual samples %s",
2846                 type == A_ASM ? "with assembler" :
2847                 type == A_SOURCE ? "with source" : "") < 0)
2848                 return 0;
2849
2850         act->fn = do_res_sample_script;
2851         act->rstype = type;
2852         return 1;
2853 }
2854
2855 static int
2856 do_switch_data(struct hist_browser *browser __maybe_unused,
2857                struct popup_action *act __maybe_unused)
2858 {
2859         if (switch_data_file()) {
2860                 ui__warning("Won't switch the data files due to\n"
2861                             "no valid data file get selected!\n");
2862                 return 0;
2863         }
2864
2865         return K_SWITCH_INPUT_DATA;
2866 }
2867
2868 static int
2869 add_switch_opt(struct hist_browser *browser,
2870                struct popup_action *act, char **optstr)
2871 {
2872         if (!is_report_browser(browser->hbt))
2873                 return 0;
2874
2875         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2876                 return 0;
2877
2878         act->fn = do_switch_data;
2879         return 1;
2880 }
2881
2882 static int
2883 do_exit_browser(struct hist_browser *browser __maybe_unused,
2884                 struct popup_action *act __maybe_unused)
2885 {
2886         return 0;
2887 }
2888
2889 static int
2890 add_exit_opt(struct hist_browser *browser __maybe_unused,
2891              struct popup_action *act, char **optstr)
2892 {
2893         if (asprintf(optstr, "Exit") < 0)
2894                 return 0;
2895
2896         act->fn = do_exit_browser;
2897         return 1;
2898 }
2899
2900 static int
2901 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2902 {
2903         if (!hists__has(browser->hists, socket) || act->socket < 0)
2904                 return 0;
2905
2906         if (browser->hists->socket_filter > -1) {
2907                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2908                 browser->hists->socket_filter = -1;
2909                 perf_hpp__set_elide(HISTC_SOCKET, false);
2910         } else {
2911                 browser->hists->socket_filter = act->socket;
2912                 perf_hpp__set_elide(HISTC_SOCKET, true);
2913                 pstack__push(browser->pstack, &browser->hists->socket_filter);
2914         }
2915
2916         hists__filter_by_socket(browser->hists);
2917         hist_browser__reset(browser);
2918         return 0;
2919 }
2920
2921 static int
2922 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2923                char **optstr, int socket_id)
2924 {
2925         if (!hists__has(browser->hists, socket) || socket_id < 0)
2926                 return 0;
2927
2928         if (asprintf(optstr, "Zoom %s Processor Socket %d",
2929                      (browser->hists->socket_filter > -1) ? "out of" : "into",
2930                      socket_id) < 0)
2931                 return 0;
2932
2933         act->socket = socket_id;
2934         act->fn = do_zoom_socket;
2935         return 1;
2936 }
2937
2938 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2939 {
2940         u64 nr_entries = 0;
2941         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2942
2943         if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2944                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2945                 return;
2946         }
2947
2948         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2949                 nr_entries++;
2950                 nd = rb_hierarchy_next(nd);
2951         }
2952
2953         hb->nr_non_filtered_entries = nr_entries;
2954         hb->nr_hierarchy_entries = nr_entries;
2955 }
2956
2957 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2958                                                double percent)
2959 {
2960         struct hist_entry *he;
2961         struct rb_node *nd = rb_first_cached(&hb->hists->entries);
2962         u64 total = hists__total_period(hb->hists);
2963         u64 min_callchain_hits = total * (percent / 100);
2964
2965         hb->min_pcnt = callchain_param.min_percent = percent;
2966
2967         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2968                 he = rb_entry(nd, struct hist_entry, rb_node);
2969
2970                 if (he->has_no_entry) {
2971                         he->has_no_entry = false;
2972                         he->nr_rows = 0;
2973                 }
2974
2975                 if (!he->leaf || !hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
2976                         goto next;
2977
2978                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2979                         total = he->stat.period;
2980
2981                         if (symbol_conf.cumulate_callchain)
2982                                 total = he->stat_acc->period;
2983
2984                         min_callchain_hits = total * (percent / 100);
2985                 }
2986
2987                 callchain_param.sort(&he->sorted_chain, he->callchain,
2988                                      min_callchain_hits, &callchain_param);
2989
2990 next:
2991                 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2992
2993                 /* force to re-evaluate folding state of callchains */
2994                 he->init_have_children = false;
2995                 hist_entry__set_folding(he, hb, false);
2996         }
2997 }
2998
2999 static int evsel__hists_browse(struct evsel *evsel, int nr_events, const char *helpline,
3000                                bool left_exits, struct hist_browser_timer *hbt, float min_pcnt,
3001                                struct perf_env *env, bool warn_lost_event)
3002 {
3003         struct hists *hists = evsel__hists(evsel);
3004         struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
3005         struct branch_info *bi = NULL;
3006 #define MAX_OPTIONS  16
3007         char *options[MAX_OPTIONS];
3008         struct popup_action actions[MAX_OPTIONS];
3009         int nr_options = 0;
3010         int key = -1;
3011         char buf[128];
3012         int delay_secs = hbt ? hbt->refresh : 0;
3013
3014 #define HIST_BROWSER_HELP_COMMON                                        \
3015         "h/?/F1        Show this window\n"                              \
3016         "UP/DOWN/PGUP\n"                                                \
3017         "PGDN/SPACE    Navigate\n"                                      \
3018         "q/ESC/CTRL+C  Exit browser or go back to previous screen\n\n"  \
3019         "For multiple event sessions:\n\n"                              \
3020         "TAB/UNTAB     Switch events\n\n"                               \
3021         "For symbolic views (--sort has sym):\n\n"                      \
3022         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
3023         "ESC           Zoom out\n"                                      \
3024         "+             Expand/Collapse one callchain level\n"           \
3025         "a             Annotate current symbol\n"                       \
3026         "C             Collapse all callchains\n"                       \
3027         "d             Zoom into current DSO\n"                         \
3028         "e             Expand/Collapse main entry callchains\n" \
3029         "E             Expand all callchains\n"                         \
3030         "F             Toggle percentage of filtered entries\n"         \
3031         "H             Display column headers\n"                        \
3032         "k             Zoom into the kernel map\n"                      \
3033         "L             Change percent limit\n"                          \
3034         "m             Display context menu\n"                          \
3035         "S             Zoom into current Processor Socket\n"            \
3036
3037         /* help messages are sorted by lexical order of the hotkey */
3038         static const char report_help[] = HIST_BROWSER_HELP_COMMON
3039         "i             Show header information\n"
3040         "P             Print histograms to perf.hist.N\n"
3041         "r             Run available scripts\n"
3042         "s             Switch to another data file in PWD\n"
3043         "t             Zoom into current Thread\n"
3044         "V             Verbose (DSO names in callchains, etc)\n"
3045         "/             Filter symbol by name\n"
3046         "0-9           Sort by event n in group";
3047         static const char top_help[] = HIST_BROWSER_HELP_COMMON
3048         "P             Print histograms to perf.hist.N\n"
3049         "t             Zoom into current Thread\n"
3050         "V             Verbose (DSO names in callchains, etc)\n"
3051         "z             Toggle zeroing of samples\n"
3052         "f             Enable/Disable events\n"
3053         "/             Filter symbol by name";
3054
3055         if (browser == NULL)
3056                 return -1;
3057
3058         /* reset abort key so that it can get Ctrl-C as a key */
3059         SLang_reset_tty();
3060         SLang_init_tty(0, 0, 0);
3061         SLtty_set_suspend_state(true);
3062
3063         if (min_pcnt)
3064                 browser->min_pcnt = min_pcnt;
3065         hist_browser__update_nr_entries(browser);
3066
3067         browser->pstack = pstack__new(3);
3068         if (browser->pstack == NULL)
3069                 goto out;
3070
3071         ui_helpline__push(helpline);
3072
3073         memset(options, 0, sizeof(options));
3074         memset(actions, 0, sizeof(actions));
3075
3076         if (symbol_conf.col_width_list_str)
3077                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
3078
3079         if (!is_report_browser(hbt))
3080                 browser->b.no_samples_msg = "Collecting samples...";
3081
3082         while (1) {
3083                 struct thread *thread = NULL;
3084                 struct map *map = NULL;
3085                 int choice;
3086                 int socked_id = -1;
3087
3088                 key = 0; // reset key
3089 do_hotkey:               // key came straight from options ui__popup_menu()
3090                 choice = nr_options = 0;
3091                 key = hist_browser__run(browser, helpline, warn_lost_event, key);
3092
3093                 if (browser->he_selection != NULL) {
3094                         thread = hist_browser__selected_thread(browser);
3095                         map = browser->selection->map;
3096                         socked_id = browser->he_selection->socket;
3097                 }
3098                 switch (key) {
3099                 case K_TAB:
3100                 case K_UNTAB:
3101                         if (nr_events == 1)
3102                                 continue;
3103                         /*
3104                          * Exit the browser, let hists__browser_tree
3105                          * go to the next or previous
3106                          */
3107                         goto out_free_stack;
3108                 case '0' ... '9':
3109                         if (!symbol_conf.event_group ||
3110                             evsel->core.nr_members < 2) {
3111                                 snprintf(buf, sizeof(buf),
3112                                          "Sort by index only available with group events!");
3113                                 helpline = buf;
3114                                 continue;
3115                         }
3116
3117                         if (key - '0' == symbol_conf.group_sort_idx)
3118                                 continue;
3119
3120                         symbol_conf.group_sort_idx = key - '0';
3121
3122                         if (symbol_conf.group_sort_idx >= evsel->core.nr_members) {
3123                                 snprintf(buf, sizeof(buf),
3124                                          "Max event group index to sort is %d (index from 0 to %d)",
3125                                          evsel->core.nr_members - 1,
3126                                          evsel->core.nr_members - 1);
3127                                 helpline = buf;
3128                                 continue;
3129                         }
3130
3131                         key = K_RELOAD;
3132                         goto out_free_stack;
3133                 case 'a':
3134                         if (!hists__has(hists, sym)) {
3135                                 ui_browser__warning(&browser->b, delay_secs * 2,
3136                         "Annotation is only available for symbolic views, "
3137                         "include \"sym*\" in --sort to use it.");
3138                                 continue;
3139                         }
3140
3141                         if (!browser->selection ||
3142                             !browser->selection->map ||
3143                             !map__dso(browser->selection->map) ||
3144                             dso__annotate_warned(map__dso(browser->selection->map))) {
3145                                 continue;
3146                         }
3147
3148                         if (!browser->selection->sym) {
3149                                 if (!browser->he_selection)
3150                                         continue;
3151
3152                                 if (sort__mode == SORT_MODE__BRANCH) {
3153                                         bi = browser->he_selection->branch_info;
3154                                         if (!bi || !bi->to.ms.map)
3155                                                 continue;
3156
3157                                         actions->ms.sym = symbol__new_unresolved(bi->to.al_addr, bi->to.ms.map);
3158                                         actions->ms.map = bi->to.ms.map;
3159                                 } else {
3160                                         actions->ms.sym = symbol__new_unresolved(browser->he_selection->ip,
3161                                                                                  browser->selection->map);
3162                                         actions->ms.map = browser->selection->map;
3163                                 }
3164
3165                                 if (!actions->ms.sym)
3166                                         continue;
3167                         } else {
3168                                 if (symbol__annotation(browser->selection->sym)->src == NULL) {
3169                                         ui_browser__warning(&browser->b, delay_secs * 2,
3170                                                 "No samples for the \"%s\" symbol.\n\n"
3171                                                 "Probably appeared just in a callchain",
3172                                                 browser->selection->sym->name);
3173                                         continue;
3174                                 }
3175
3176                                 actions->ms.map = browser->selection->map;
3177                                 actions->ms.sym = browser->selection->sym;
3178                         }
3179
3180                         do_annotate(browser, actions);
3181                         continue;
3182                 case 'P':
3183                         hist_browser__dump(browser);
3184                         continue;
3185                 case 'd':
3186                         actions->ms.map = map;
3187                         do_zoom_dso(browser, actions);
3188                         continue;
3189                 case 'k':
3190                         if (browser->selection != NULL)
3191                                 hists_browser__zoom_map(browser,
3192                                               maps__machine(browser->selection->maps)->vmlinux_map);
3193                         continue;
3194                 case 'V':
3195                         verbose = (verbose + 1) % 4;
3196                         browser->show_dso = verbose > 0;
3197                         ui_helpline__fpush("Verbosity level set to %d\n",
3198                                            verbose);
3199                         continue;
3200                 case 't':
3201                         actions->thread = thread;
3202                         do_zoom_thread(browser, actions);
3203                         continue;
3204                 case 'S':
3205                         actions->socket = socked_id;
3206                         do_zoom_socket(browser, actions);
3207                         continue;
3208                 case '/':
3209                         if (ui_browser__input_window("Symbol to show",
3210                                         "Please enter the name of symbol you want to see.\n"
3211                                         "To remove the filter later, press / + ENTER.",
3212                                         buf, "ENTER: OK, ESC: Cancel",
3213                                         delay_secs * 2) == K_ENTER) {
3214                                 hists->symbol_filter_str = *buf ? buf : NULL;
3215                                 hists__filter_by_symbol(hists);
3216                                 hist_browser__reset(browser);
3217                         }
3218                         continue;
3219                 case 'r':
3220                         if (is_report_browser(hbt)) {
3221                                 actions->thread = NULL;
3222                                 actions->ms.sym = NULL;
3223                                 do_run_script(browser, actions);
3224                         }
3225                         continue;
3226                 case 's':
3227                         if (is_report_browser(hbt)) {
3228                                 key = do_switch_data(browser, actions);
3229                                 if (key == K_SWITCH_INPUT_DATA)
3230                                         goto out_free_stack;
3231                         }
3232                         continue;
3233                 case 'i':
3234                         /* env->arch is NULL for live-mode (i.e. perf top) */
3235                         if (env->arch)
3236                                 tui__header_window(env);
3237                         continue;
3238                 case 'F':
3239                         symbol_conf.filter_relative ^= 1;
3240                         continue;
3241                 case 'z':
3242                         if (!is_report_browser(hbt)) {
3243                                 struct perf_top *top = hbt->arg;
3244
3245                                 top->zero = !top->zero;
3246                         }
3247                         continue;
3248                 case 'L':
3249                         if (ui_browser__input_window("Percent Limit",
3250                                         "Please enter the value you want to hide entries under that percent.",
3251                                         buf, "ENTER: OK, ESC: Cancel",
3252                                         delay_secs * 2) == K_ENTER) {
3253                                 char *end;
3254                                 double new_percent = strtod(buf, &end);
3255
3256                                 if (new_percent < 0 || new_percent > 100) {
3257                                         ui_browser__warning(&browser->b, delay_secs * 2,
3258                                                 "Invalid percent: %.2f", new_percent);
3259                                         continue;
3260                                 }
3261
3262                                 hist_browser__update_percent_limit(browser, new_percent);
3263                                 hist_browser__reset(browser);
3264                         }
3265                         continue;
3266                 case K_F1:
3267                 case 'h':
3268                 case '?':
3269                         ui_browser__help_window(&browser->b,
3270                                 is_report_browser(hbt) ? report_help : top_help);
3271                         continue;
3272                 case K_ENTER:
3273                 case K_RIGHT:
3274                 case 'm':
3275                         /* menu */
3276                         break;
3277                 case K_ESC:
3278                 case K_LEFT: {
3279                         const void *top;
3280
3281                         if (pstack__empty(browser->pstack)) {
3282                                 /*
3283                                  * Go back to the perf_evsel_menu__run or other user
3284                                  */
3285                                 if (left_exits)
3286                                         goto out_free_stack;
3287
3288                                 if (key == K_ESC &&
3289                                     ui_browser__dialog_yesno(&browser->b,
3290                                                              "Do you really want to exit?"))
3291                                         goto out_free_stack;
3292
3293                                 continue;
3294                         }
3295                         actions->ms.map = map;
3296                         top = pstack__peek(browser->pstack);
3297                         if (top == &browser->hists->dso_filter) {
3298                                 /*
3299                                  * No need to set actions->dso here since
3300                                  * it's just to remove the current filter.
3301                                  */
3302                                 do_zoom_dso(browser, actions);
3303                         } else if (top == &browser->hists->thread_filter) {
3304                                 actions->thread = thread;
3305                                 do_zoom_thread(browser, actions);
3306                         } else if (top == &browser->hists->socket_filter) {
3307                                 do_zoom_socket(browser, actions);
3308                         }
3309                         continue;
3310                 }
3311                 case 'q':
3312                 case CTRL('c'):
3313                         goto out_free_stack;
3314                 case 'f':
3315                         if (!is_report_browser(hbt)) {
3316                                 struct perf_top *top = hbt->arg;
3317
3318                                 evlist__toggle_enable(top->evlist);
3319                                 /*
3320                                  * No need to refresh, resort/decay histogram
3321                                  * entries if we are not collecting samples:
3322                                  */
3323                                 if (top->evlist->enabled) {
3324                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3325                                         hbt->refresh = delay_secs;
3326                                 } else {
3327                                         helpline = "Press 'f' again to re-enable the events";
3328                                         hbt->refresh = 0;
3329                                 }
3330                                 continue;
3331                         }
3332                         /* Fall thru */
3333                 default:
3334                         helpline = "Press '?' for help on key bindings";
3335                         ui_browser__warn_unhandled_hotkey(&browser->b, key, delay_secs,
3336                                                           ", use 'h'/'?'/F1 to see actions");
3337                         continue;
3338                 }
3339
3340                 if (!hists__has(hists, sym) || browser->selection == NULL)
3341                         goto skip_annotation;
3342
3343                 if (sort__mode == SORT_MODE__BRANCH) {
3344
3345                         if (browser->he_selection)
3346                                 bi = browser->he_selection->branch_info;
3347
3348                         if (bi == NULL)
3349                                 goto skip_annotation;
3350
3351                         nr_options += add_annotate_opt(&actions[nr_options],
3352                                                        &options[nr_options],
3353                                                        &bi->from.ms,
3354                                                        bi->from.al_addr);
3355                         if (bi->to.ms.sym != bi->from.ms.sym)
3356                                 nr_options += add_annotate_opt(&actions[nr_options],
3357                                                         &options[nr_options],
3358                                                         &bi->to.ms,
3359                                                         bi->to.al_addr);
3360                 } else if (browser->he_selection) {
3361                         nr_options += add_annotate_opt(&actions[nr_options],
3362                                                        &options[nr_options],
3363                                                        browser->selection,
3364                                                        browser->he_selection->ip);
3365                 }
3366 skip_annotation:
3367                 nr_options += add_annotate_type_opt(&actions[nr_options],
3368                                                     &options[nr_options],
3369                                                     browser->he_selection);
3370                 nr_options += add_thread_opt(browser, &actions[nr_options],
3371                                              &options[nr_options], thread);
3372                 nr_options += add_dso_opt(browser, &actions[nr_options],
3373                                           &options[nr_options], map);
3374                 nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]);
3375                 nr_options += add_map_opt(browser, &actions[nr_options],
3376                                           &options[nr_options],
3377                                           browser->selection ?
3378                                                 browser->selection->map : NULL);
3379                 nr_options += add_socket_opt(browser, &actions[nr_options],
3380                                              &options[nr_options],
3381                                              socked_id);
3382                 /* perf script support */
3383                 if (!is_report_browser(hbt))
3384                         goto skip_scripting;
3385
3386                 if (browser->he_selection) {
3387                         if (hists__has(hists, thread) && thread) {
3388                                 nr_options += add_script_opt(browser,
3389                                                              &actions[nr_options],
3390                                                              &options[nr_options],
3391                                                              thread, NULL);
3392                         }
3393                         /*
3394                          * Note that browser->selection != NULL
3395                          * when browser->he_selection is not NULL,
3396                          * so we don't need to check browser->selection
3397                          * before fetching browser->selection->sym like what
3398                          * we do before fetching browser->selection->map.
3399                          *
3400                          * See hist_browser__show_entry.
3401                          */
3402                         if (hists__has(hists, sym) && browser->selection->sym) {
3403                                 nr_options += add_script_opt(browser,
3404                                                              &actions[nr_options],
3405                                                              &options[nr_options],
3406                                                              NULL, browser->selection->sym);
3407                         }
3408                 }
3409                 nr_options += add_script_opt(browser, &actions[nr_options],
3410                                              &options[nr_options], NULL, NULL);
3411                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3412                                                  &options[nr_options],
3413                                                  hist_browser__selected_res_sample(browser),
3414                                                  A_NORMAL);
3415                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3416                                                  &options[nr_options],
3417                                                  hist_browser__selected_res_sample(browser),
3418                                                  A_ASM);
3419                 nr_options += add_res_sample_opt(browser, &actions[nr_options],
3420                                                  &options[nr_options],
3421                                                  hist_browser__selected_res_sample(browser),
3422                                                  A_SOURCE);
3423                 nr_options += add_switch_opt(browser, &actions[nr_options],
3424                                              &options[nr_options]);
3425 skip_scripting:
3426                 nr_options += add_exit_opt(browser, &actions[nr_options],
3427                                            &options[nr_options]);
3428
3429                 do {
3430                         struct popup_action *act;
3431
3432                         choice = ui__popup_menu(nr_options, options, &key);
3433                         if (choice == -1)
3434                                 break;
3435
3436                         if (choice == nr_options)
3437                                 goto do_hotkey;
3438
3439                         act = &actions[choice];
3440                         key = act->fn(browser, act);
3441                 } while (key == 1);
3442
3443                 if (key == K_SWITCH_INPUT_DATA)
3444                         break;
3445         }
3446 out_free_stack:
3447         pstack__delete(browser->pstack);
3448 out:
3449         hist_browser__delete(browser);
3450         free_popup_options(options, MAX_OPTIONS);
3451         return key;
3452 }
3453
3454 struct evsel_menu {
3455         struct ui_browser b;
3456         struct evsel *selection;
3457         bool lost_events, lost_events_warned;
3458         float min_pcnt;
3459         struct perf_env *env;
3460 };
3461
3462 static void perf_evsel_menu__write(struct ui_browser *browser,
3463                                    void *entry, int row)
3464 {
3465         struct evsel_menu *menu = container_of(browser,
3466                                                     struct evsel_menu, b);
3467         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3468         struct hists *hists = evsel__hists(evsel);
3469         bool current_entry = ui_browser__is_current_entry(browser, row);
3470         unsigned long nr_events = hists->stats.nr_samples;
3471         const char *ev_name = evsel__name(evsel);
3472         char bf[256], unit;
3473         const char *warn = " ";
3474         size_t printed;
3475
3476         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3477                                                        HE_COLORSET_NORMAL);
3478
3479         if (evsel__is_group_event(evsel)) {
3480                 struct evsel *pos;
3481
3482                 ev_name = evsel__group_name(evsel);
3483
3484                 for_each_group_member(pos, evsel) {
3485                         struct hists *pos_hists = evsel__hists(pos);
3486                         nr_events += pos_hists->stats.nr_samples;
3487                 }
3488         }
3489
3490         nr_events = convert_unit(nr_events, &unit);
3491         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3492                            unit, unit == ' ' ? "" : " ", ev_name);
3493         ui_browser__printf(browser, "%s", bf);
3494
3495         nr_events = evsel->evlist->stats.nr_events[PERF_RECORD_LOST];
3496         if (nr_events != 0) {
3497                 menu->lost_events = true;
3498                 if (!current_entry)
3499                         ui_browser__set_color(browser, HE_COLORSET_TOP);
3500                 nr_events = convert_unit(nr_events, &unit);
3501                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3502                                      nr_events, unit, unit == ' ' ? "" : " ");
3503                 warn = bf;
3504         }
3505
3506         ui_browser__write_nstring(browser, warn, browser->width - printed);
3507
3508         if (current_entry)
3509                 menu->selection = evsel;
3510 }
3511
3512 static int perf_evsel_menu__run(struct evsel_menu *menu,
3513                                 int nr_events, const char *help,
3514                                 struct hist_browser_timer *hbt,
3515                                 bool warn_lost_event)
3516 {
3517         struct evlist *evlist = menu->b.priv;
3518         struct evsel *pos;
3519         const char *title = "Available samples";
3520         int delay_secs = hbt ? hbt->refresh : 0;
3521         int key;
3522
3523         if (ui_browser__show(&menu->b, title,
3524                              "ESC: exit, ENTER|->: Browse histograms") < 0)
3525                 return -1;
3526
3527         while (1) {
3528                 key = ui_browser__run(&menu->b, delay_secs);
3529
3530                 switch (key) {
3531                 case K_TIMER:
3532                         if (hbt)
3533                                 hbt->timer(hbt->arg);
3534
3535                         if (!menu->lost_events_warned &&
3536                             menu->lost_events &&
3537                             warn_lost_event) {
3538                                 ui_browser__warn_lost_events(&menu->b);
3539                                 menu->lost_events_warned = true;
3540                         }
3541                         continue;
3542                 case K_RIGHT:
3543                 case K_ENTER:
3544                         if (!menu->selection)
3545                                 continue;
3546                         pos = menu->selection;
3547 browse_hists:
3548                         evlist__set_selected(evlist, pos);
3549                         /*
3550                          * Give the calling tool a chance to populate the non
3551                          * default evsel resorted hists tree.
3552                          */
3553                         if (hbt)
3554                                 hbt->timer(hbt->arg);
3555                         key = evsel__hists_browse(pos, nr_events, help, true, hbt,
3556                                                   menu->min_pcnt, menu->env,
3557                                                   warn_lost_event);
3558                         ui_browser__show_title(&menu->b, title);
3559                         switch (key) {
3560                         case K_TAB:
3561                                 if (pos->core.node.next == &evlist->core.entries)
3562                                         pos = evlist__first(evlist);
3563                                 else
3564                                         pos = evsel__next(pos);
3565                                 goto browse_hists;
3566                         case K_UNTAB:
3567                                 if (pos->core.node.prev == &evlist->core.entries)
3568                                         pos = evlist__last(evlist);
3569                                 else
3570                                         pos = evsel__prev(pos);
3571                                 goto browse_hists;
3572                         case K_SWITCH_INPUT_DATA:
3573                         case K_RELOAD:
3574                         case 'q':
3575                         case CTRL('c'):
3576                                 goto out;
3577                         case K_ESC:
3578                         default:
3579                                 continue;
3580                         }
3581                 case K_LEFT:
3582                         continue;
3583                 case K_ESC:
3584                         if (!ui_browser__dialog_yesno(&menu->b,
3585                                                "Do you really want to exit?"))
3586                                 continue;
3587                         /* Fall thru */
3588                 case 'q':
3589                 case CTRL('c'):
3590                         goto out;
3591                 default:
3592                         ui_browser__warn_unhandled_hotkey(&menu->b, key, delay_secs, NULL);
3593                         continue;
3594                 }
3595         }
3596
3597 out:
3598         ui_browser__hide(&menu->b);
3599         return key;
3600 }
3601
3602 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3603                                  void *entry)
3604 {
3605         struct evsel *evsel = list_entry(entry, struct evsel, core.node);
3606
3607         if (symbol_conf.event_group && !evsel__is_group_leader(evsel))
3608                 return true;
3609
3610         return false;
3611 }
3612
3613 static int __evlist__tui_browse_hists(struct evlist *evlist, int nr_entries, const char *help,
3614                                       struct hist_browser_timer *hbt, float min_pcnt, struct perf_env *env,
3615                                       bool warn_lost_event)
3616 {
3617         struct evsel *pos;
3618         struct evsel_menu menu = {
3619                 .b = {
3620                         .entries    = &evlist->core.entries,
3621                         .refresh    = ui_browser__list_head_refresh,
3622                         .seek       = ui_browser__list_head_seek,
3623                         .write      = perf_evsel_menu__write,
3624                         .filter     = filter_group_entries,
3625                         .nr_entries = nr_entries,
3626                         .priv       = evlist,
3627                 },
3628                 .min_pcnt = min_pcnt,
3629                 .env = env,
3630         };
3631
3632         ui_helpline__push("Press ESC to exit");
3633
3634         evlist__for_each_entry(evlist, pos) {
3635                 const char *ev_name = evsel__name(pos);
3636                 size_t line_len = strlen(ev_name) + 7;
3637
3638                 if (menu.b.width < line_len)
3639                         menu.b.width = line_len;
3640         }
3641
3642         return perf_evsel_menu__run(&menu, nr_entries, help,
3643                                     hbt, warn_lost_event);
3644 }
3645
3646 static bool evlist__single_entry(struct evlist *evlist)
3647 {
3648         int nr_entries = evlist->core.nr_entries;
3649
3650         if (nr_entries == 1)
3651                return true;
3652
3653         if (nr_entries == 2) {
3654                 struct evsel *last = evlist__last(evlist);
3655
3656                 if (evsel__is_dummy_event(last))
3657                         return true;
3658         }
3659
3660         return false;
3661 }
3662
3663 int evlist__tui_browse_hists(struct evlist *evlist, const char *help, struct hist_browser_timer *hbt,
3664                              float min_pcnt, struct perf_env *env, bool warn_lost_event)
3665 {
3666         int nr_entries = evlist->core.nr_entries;
3667
3668         if (evlist__single_entry(evlist)) {
3669 single_entry: {
3670                 struct evsel *first = evlist__first(evlist);
3671
3672                 return evsel__hists_browse(first, nr_entries, help, false, hbt, min_pcnt,
3673                                            env, warn_lost_event);
3674         }
3675         }
3676
3677         if (symbol_conf.event_group) {
3678                 struct evsel *pos;
3679
3680                 nr_entries = 0;
3681                 evlist__for_each_entry(evlist, pos) {
3682                         if (evsel__is_group_leader(pos))
3683                                 nr_entries++;
3684                 }
3685
3686                 if (nr_entries == 1)
3687                         goto single_entry;
3688         }
3689
3690         return __evlist__tui_browse_hists(evlist, nr_entries, help, hbt, min_pcnt, env,
3691                                           warn_lost_event);
3692 }
3693
3694 static int block_hists_browser__title(struct hist_browser *browser, char *bf,
3695                                       size_t size)
3696 {
3697         struct hists *hists = evsel__hists(browser->block_evsel);
3698         const char *evname = evsel__name(browser->block_evsel);
3699         unsigned long nr_samples = hists->stats.nr_samples;
3700         int ret;
3701
3702         ret = scnprintf(bf, size, "# Samples: %lu", nr_samples);
3703         if (evname)
3704                 scnprintf(bf + ret, size -  ret, " of event '%s'", evname);
3705
3706         return 0;
3707 }
3708
3709 int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel,
3710                            float min_percent, struct perf_env *env)
3711 {
3712         struct hists *hists = &bh->block_hists;
3713         struct hist_browser *browser;
3714         int key = -1;
3715         struct popup_action action;
3716         char *br_cntr_text = NULL;
3717         static const char help[] =
3718         " q/ESC         Quit \n"
3719         " B             Branch counter abbr list (Optional)\n";
3720
3721         browser = hist_browser__new(hists);
3722         if (!browser)
3723                 return -1;
3724
3725         browser->block_evsel = evsel;
3726         browser->title = block_hists_browser__title;
3727         browser->min_pcnt = min_percent;
3728         browser->env = env;
3729
3730         /* reset abort key so that it can get Ctrl-C as a key */
3731         SLang_reset_tty();
3732         SLang_init_tty(0, 0, 0);
3733         SLtty_set_suspend_state(true);
3734
3735         memset(&action, 0, sizeof(action));
3736
3737         if (!annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false))
3738                 annotate_opts.show_br_cntr = true;
3739
3740         while (1) {
3741                 key = hist_browser__run(browser, "? - help", true, 0);
3742
3743                 switch (key) {
3744                 case 'q':
3745                 case K_ESC:
3746                         goto out;
3747                 case '?':
3748                         ui_browser__help_window(&browser->b, help);
3749                         break;
3750                 case 'a':
3751                 case K_ENTER:
3752                         if (!browser->selection ||
3753                             !browser->selection->sym) {
3754                                 continue;
3755                         }
3756
3757                         action.ms.map = browser->selection->map;
3758                         action.ms.sym = browser->selection->sym;
3759                         do_annotate(browser, &action);
3760                         continue;
3761                 case 'B':
3762                         if (br_cntr_text) {
3763                                 ui__question_window("Branch counter abbr list",
3764                                                     br_cntr_text, "Press any key...", 0);
3765                         } else {
3766                                 ui__question_window("Branch counter abbr list",
3767                                                     "\n The branch counter is not available.\n",
3768                                                     "Press any key...", 0);
3769                         }
3770                         continue;
3771                 default:
3772                         ui_browser__warn_unhandled_hotkey(&browser->b, key, 0,
3773                                                           ", use '?' to see actions");
3774                         continue;
3775                 }
3776         }
3777
3778 out:
3779         hist_browser__delete(browser);
3780         free(br_cntr_text);
3781         return 0;
3782 }