OSDN Git Service

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