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