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