]> Git Repo - linux.git/blob - tools/perf/ui/browsers/hists.c
Merge tag 'for-linus-20160112' of git://git.infradead.org/linux-mtd
[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 "../browser.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
21
22 struct hist_browser {
23         struct ui_browser   b;
24         struct hists        *hists;
25         struct hist_entry   *he_selection;
26         struct map_symbol   *selection;
27         struct hist_browser_timer *hbt;
28         struct pstack       *pstack;
29         struct perf_env *env;
30         int                  print_seq;
31         bool                 show_dso;
32         bool                 show_headers;
33         float                min_pcnt;
34         u64                  nr_non_filtered_entries;
35         u64                  nr_callchain_rows;
36 };
37
38 extern void hist_browser__init_hpp(void);
39
40 static int hists__browser_title(struct hists *hists,
41                                 struct hist_browser_timer *hbt,
42                                 char *bf, size_t size);
43 static void hist_browser__update_nr_entries(struct hist_browser *hb);
44
45 static struct rb_node *hists__filter_entries(struct rb_node *nd,
46                                              float min_pcnt);
47
48 static bool hist_browser__has_filter(struct hist_browser *hb)
49 {
50         return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
51 }
52
53 static int hist_browser__get_folding(struct hist_browser *browser)
54 {
55         struct rb_node *nd;
56         struct hists *hists = browser->hists;
57         int unfolded_rows = 0;
58
59         for (nd = rb_first(&hists->entries);
60              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
61              nd = rb_next(nd)) {
62                 struct hist_entry *he =
63                         rb_entry(nd, struct hist_entry, rb_node);
64
65                 if (he->unfolded)
66                         unfolded_rows += he->nr_rows;
67         }
68         return unfolded_rows;
69 }
70
71 static u32 hist_browser__nr_entries(struct hist_browser *hb)
72 {
73         u32 nr_entries;
74
75         if (hist_browser__has_filter(hb))
76                 nr_entries = hb->nr_non_filtered_entries;
77         else
78                 nr_entries = hb->hists->nr_entries;
79
80         hb->nr_callchain_rows = hist_browser__get_folding(hb);
81         return nr_entries + hb->nr_callchain_rows;
82 }
83
84 static void hist_browser__update_rows(struct hist_browser *hb)
85 {
86         struct ui_browser *browser = &hb->b;
87         u16 header_offset = hb->show_headers ? 1 : 0, index_row;
88
89         browser->rows = browser->height - header_offset;
90         /*
91          * Verify if we were at the last line and that line isn't
92          * visibe because we now show the header line(s).
93          */
94         index_row = browser->index - browser->top_idx;
95         if (index_row >= browser->rows)
96                 browser->index -= index_row - browser->rows + 1;
97 }
98
99 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
100 {
101         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
102
103         /* 3 == +/- toggle symbol before actual hist_entry rendering */
104         browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
105         /*
106          * FIXME: Just keeping existing behaviour, but this really should be
107          *        before updating browser->width, as it will invalidate the
108          *        calculation above. Fix this and the fallout in another
109          *        changeset.
110          */
111         ui_browser__refresh_dimensions(browser);
112         hist_browser__update_rows(hb);
113 }
114
115 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
116 {
117         u16 header_offset = browser->show_headers ? 1 : 0;
118
119         ui_browser__gotorc(&browser->b, row + header_offset, column);
120 }
121
122 static void hist_browser__reset(struct hist_browser *browser)
123 {
124         /*
125          * The hists__remove_entry_filter() already folds non-filtered
126          * entries so we can assume it has 0 callchain rows.
127          */
128         browser->nr_callchain_rows = 0;
129
130         hist_browser__update_nr_entries(browser);
131         browser->b.nr_entries = hist_browser__nr_entries(browser);
132         hist_browser__refresh_dimensions(&browser->b);
133         ui_browser__reset_index(&browser->b);
134 }
135
136 static char tree__folded_sign(bool unfolded)
137 {
138         return unfolded ? '-' : '+';
139 }
140
141 static char hist_entry__folded(const struct hist_entry *he)
142 {
143         return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
144 }
145
146 static char callchain_list__folded(const struct callchain_list *cl)
147 {
148         return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
149 }
150
151 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
152 {
153         cl->unfolded = unfold ? cl->has_children : false;
154 }
155
156 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
157 {
158         int n = 0;
159         struct rb_node *nd;
160
161         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
162                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
163                 struct callchain_list *chain;
164                 char folded_sign = ' '; /* No children */
165
166                 list_for_each_entry(chain, &child->val, list) {
167                         ++n;
168                         /* We need this because we may not have children */
169                         folded_sign = callchain_list__folded(chain);
170                         if (folded_sign == '+')
171                                 break;
172                 }
173
174                 if (folded_sign == '-') /* Have children and they're unfolded */
175                         n += callchain_node__count_rows_rb_tree(child);
176         }
177
178         return n;
179 }
180
181 static int callchain_node__count_flat_rows(struct callchain_node *node)
182 {
183         struct callchain_list *chain;
184         char folded_sign = 0;
185         int n = 0;
186
187         list_for_each_entry(chain, &node->parent_val, list) {
188                 if (!folded_sign) {
189                         /* only check first chain list entry */
190                         folded_sign = callchain_list__folded(chain);
191                         if (folded_sign == '+')
192                                 return 1;
193                 }
194                 n++;
195         }
196
197         list_for_each_entry(chain, &node->val, list) {
198                 if (!folded_sign) {
199                         /* node->parent_val list might be empty */
200                         folded_sign = callchain_list__folded(chain);
201                         if (folded_sign == '+')
202                                 return 1;
203                 }
204                 n++;
205         }
206
207         return n;
208 }
209
210 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
211 {
212         return 1;
213 }
214
215 static int callchain_node__count_rows(struct callchain_node *node)
216 {
217         struct callchain_list *chain;
218         bool unfolded = false;
219         int n = 0;
220
221         if (callchain_param.mode == CHAIN_FLAT)
222                 return callchain_node__count_flat_rows(node);
223         else if (callchain_param.mode == CHAIN_FOLDED)
224                 return callchain_node__count_folded_rows(node);
225
226         list_for_each_entry(chain, &node->val, list) {
227                 ++n;
228                 unfolded = chain->unfolded;
229         }
230
231         if (unfolded)
232                 n += callchain_node__count_rows_rb_tree(node);
233
234         return n;
235 }
236
237 static int callchain__count_rows(struct rb_root *chain)
238 {
239         struct rb_node *nd;
240         int n = 0;
241
242         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
243                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
244                 n += callchain_node__count_rows(node);
245         }
246
247         return n;
248 }
249
250 static bool hist_entry__toggle_fold(struct hist_entry *he)
251 {
252         if (!he)
253                 return false;
254
255         if (!he->has_children)
256                 return false;
257
258         he->unfolded = !he->unfolded;
259         return true;
260 }
261
262 static bool callchain_list__toggle_fold(struct callchain_list *cl)
263 {
264         if (!cl)
265                 return false;
266
267         if (!cl->has_children)
268                 return false;
269
270         cl->unfolded = !cl->unfolded;
271         return true;
272 }
273
274 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
275 {
276         struct rb_node *nd = rb_first(&node->rb_root);
277
278         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
279                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
280                 struct callchain_list *chain;
281                 bool first = true;
282
283                 list_for_each_entry(chain, &child->val, list) {
284                         if (first) {
285                                 first = false;
286                                 chain->has_children = chain->list.next != &child->val ||
287                                                          !RB_EMPTY_ROOT(&child->rb_root);
288                         } else
289                                 chain->has_children = chain->list.next == &child->val &&
290                                                          !RB_EMPTY_ROOT(&child->rb_root);
291                 }
292
293                 callchain_node__init_have_children_rb_tree(child);
294         }
295 }
296
297 static void callchain_node__init_have_children(struct callchain_node *node,
298                                                bool has_sibling)
299 {
300         struct callchain_list *chain;
301
302         chain = list_entry(node->val.next, struct callchain_list, list);
303         chain->has_children = has_sibling;
304
305         if (node->val.next != node->val.prev) {
306                 chain = list_entry(node->val.prev, struct callchain_list, list);
307                 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
308         }
309
310         callchain_node__init_have_children_rb_tree(node);
311 }
312
313 static void callchain__init_have_children(struct rb_root *root)
314 {
315         struct rb_node *nd = rb_first(root);
316         bool has_sibling = nd && rb_next(nd);
317
318         for (nd = rb_first(root); nd; nd = rb_next(nd)) {
319                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
320                 callchain_node__init_have_children(node, has_sibling);
321                 if (callchain_param.mode == CHAIN_FLAT ||
322                     callchain_param.mode == CHAIN_FOLDED)
323                         callchain_node__make_parent_list(node);
324         }
325 }
326
327 static void hist_entry__init_have_children(struct hist_entry *he)
328 {
329         if (!he->init_have_children) {
330                 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
331                 callchain__init_have_children(&he->sorted_chain);
332                 he->init_have_children = true;
333         }
334 }
335
336 static bool hist_browser__toggle_fold(struct hist_browser *browser)
337 {
338         struct hist_entry *he = browser->he_selection;
339         struct map_symbol *ms = browser->selection;
340         struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
341         bool has_children;
342
343         if (!he || !ms)
344                 return false;
345
346         if (ms == &he->ms)
347                 has_children = hist_entry__toggle_fold(he);
348         else
349                 has_children = callchain_list__toggle_fold(cl);
350
351         if (has_children) {
352                 hist_entry__init_have_children(he);
353                 browser->b.nr_entries -= he->nr_rows;
354                 browser->nr_callchain_rows -= he->nr_rows;
355
356                 if (he->unfolded)
357                         he->nr_rows = callchain__count_rows(&he->sorted_chain);
358                 else
359                         he->nr_rows = 0;
360
361                 browser->b.nr_entries += he->nr_rows;
362                 browser->nr_callchain_rows += he->nr_rows;
363
364                 return true;
365         }
366
367         /* If it doesn't have children, no toggling performed */
368         return false;
369 }
370
371 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
372 {
373         int n = 0;
374         struct rb_node *nd;
375
376         for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
377                 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
378                 struct callchain_list *chain;
379                 bool has_children = false;
380
381                 list_for_each_entry(chain, &child->val, list) {
382                         ++n;
383                         callchain_list__set_folding(chain, unfold);
384                         has_children = chain->has_children;
385                 }
386
387                 if (has_children)
388                         n += callchain_node__set_folding_rb_tree(child, unfold);
389         }
390
391         return n;
392 }
393
394 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
395 {
396         struct callchain_list *chain;
397         bool has_children = false;
398         int n = 0;
399
400         list_for_each_entry(chain, &node->val, list) {
401                 ++n;
402                 callchain_list__set_folding(chain, unfold);
403                 has_children = chain->has_children;
404         }
405
406         if (has_children)
407                 n += callchain_node__set_folding_rb_tree(node, unfold);
408
409         return n;
410 }
411
412 static int callchain__set_folding(struct rb_root *chain, bool unfold)
413 {
414         struct rb_node *nd;
415         int n = 0;
416
417         for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
418                 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
419                 n += callchain_node__set_folding(node, unfold);
420         }
421
422         return n;
423 }
424
425 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
426 {
427         hist_entry__init_have_children(he);
428         he->unfolded = unfold ? he->has_children : false;
429
430         if (he->has_children) {
431                 int n = callchain__set_folding(&he->sorted_chain, unfold);
432                 he->nr_rows = unfold ? n : 0;
433         } else
434                 he->nr_rows = 0;
435 }
436
437 static void
438 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
439 {
440         struct rb_node *nd;
441         struct hists *hists = browser->hists;
442
443         for (nd = rb_first(&hists->entries);
444              (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
445              nd = rb_next(nd)) {
446                 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
447                 hist_entry__set_folding(he, unfold);
448                 browser->nr_callchain_rows += he->nr_rows;
449         }
450 }
451
452 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
453 {
454         browser->nr_callchain_rows = 0;
455         __hist_browser__set_folding(browser, unfold);
456
457         browser->b.nr_entries = hist_browser__nr_entries(browser);
458         /* Go to the start, we may be way after valid entries after a collapse */
459         ui_browser__reset_index(&browser->b);
460 }
461
462 static void ui_browser__warn_lost_events(struct ui_browser *browser)
463 {
464         ui_browser__warning(browser, 4,
465                 "Events are being lost, check IO/CPU overload!\n\n"
466                 "You may want to run 'perf' using a RT scheduler policy:\n\n"
467                 " perf top -r 80\n\n"
468                 "Or reduce the sampling frequency.");
469 }
470
471 static int hist_browser__run(struct hist_browser *browser, const char *help)
472 {
473         int key;
474         char title[160];
475         struct hist_browser_timer *hbt = browser->hbt;
476         int delay_secs = hbt ? hbt->refresh : 0;
477
478         browser->b.entries = &browser->hists->entries;
479         browser->b.nr_entries = hist_browser__nr_entries(browser);
480
481         hists__browser_title(browser->hists, hbt, title, sizeof(title));
482
483         if (ui_browser__show(&browser->b, title, help) < 0)
484                 return -1;
485
486         while (1) {
487                 key = ui_browser__run(&browser->b, delay_secs);
488
489                 switch (key) {
490                 case K_TIMER: {
491                         u64 nr_entries;
492                         hbt->timer(hbt->arg);
493
494                         if (hist_browser__has_filter(browser))
495                                 hist_browser__update_nr_entries(browser);
496
497                         nr_entries = hist_browser__nr_entries(browser);
498                         ui_browser__update_nr_entries(&browser->b, nr_entries);
499
500                         if (browser->hists->stats.nr_lost_warned !=
501                             browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
502                                 browser->hists->stats.nr_lost_warned =
503                                         browser->hists->stats.nr_events[PERF_RECORD_LOST];
504                                 ui_browser__warn_lost_events(&browser->b);
505                         }
506
507                         hists__browser_title(browser->hists,
508                                              hbt, title, sizeof(title));
509                         ui_browser__show_title(&browser->b, title);
510                         continue;
511                 }
512                 case 'D': { /* Debug */
513                         static int seq;
514                         struct hist_entry *h = rb_entry(browser->b.top,
515                                                         struct hist_entry, rb_node);
516                         ui_helpline__pop();
517                         ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
518                                            seq++, browser->b.nr_entries,
519                                            browser->hists->nr_entries,
520                                            browser->b.rows,
521                                            browser->b.index,
522                                            browser->b.top_idx,
523                                            h->row_offset, h->nr_rows);
524                 }
525                         break;
526                 case 'C':
527                         /* Collapse the whole world. */
528                         hist_browser__set_folding(browser, false);
529                         break;
530                 case 'E':
531                         /* Expand the whole world. */
532                         hist_browser__set_folding(browser, true);
533                         break;
534                 case 'H':
535                         browser->show_headers = !browser->show_headers;
536                         hist_browser__update_rows(browser);
537                         break;
538                 case K_ENTER:
539                         if (hist_browser__toggle_fold(browser))
540                                 break;
541                         /* fall thru */
542                 default:
543                         goto out;
544                 }
545         }
546 out:
547         ui_browser__hide(&browser->b);
548         return key;
549 }
550
551 struct callchain_print_arg {
552         /* for hists browser */
553         off_t   row_offset;
554         bool    is_current_entry;
555
556         /* for file dump */
557         FILE    *fp;
558         int     printed;
559 };
560
561 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
562                                          struct callchain_list *chain,
563                                          const char *str, int offset,
564                                          unsigned short row,
565                                          struct callchain_print_arg *arg);
566
567 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
568                                                struct callchain_list *chain,
569                                                const char *str, int offset,
570                                                unsigned short row,
571                                                struct callchain_print_arg *arg)
572 {
573         int color, width;
574         char folded_sign = callchain_list__folded(chain);
575         bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
576
577         color = HE_COLORSET_NORMAL;
578         width = browser->b.width - (offset + 2);
579         if (ui_browser__is_current_entry(&browser->b, row)) {
580                 browser->selection = &chain->ms;
581                 color = HE_COLORSET_SELECTED;
582                 arg->is_current_entry = true;
583         }
584
585         ui_browser__set_color(&browser->b, color);
586         hist_browser__gotorc(browser, row, 0);
587         ui_browser__write_nstring(&browser->b, " ", offset);
588         ui_browser__printf(&browser->b, "%c", folded_sign);
589         ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
590         ui_browser__write_nstring(&browser->b, str, width);
591 }
592
593 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
594                                                   struct callchain_list *chain,
595                                                   const char *str, int offset,
596                                                   unsigned short row __maybe_unused,
597                                                   struct callchain_print_arg *arg)
598 {
599         char folded_sign = callchain_list__folded(chain);
600
601         arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
602                                 folded_sign, str);
603 }
604
605 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
606                                      unsigned short row);
607
608 static bool hist_browser__check_output_full(struct hist_browser *browser,
609                                             unsigned short row)
610 {
611         return browser->b.rows == row;
612 }
613
614 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
615                                           unsigned short row __maybe_unused)
616 {
617         return false;
618 }
619
620 #define LEVEL_OFFSET_STEP 3
621
622 static int hist_browser__show_callchain_list(struct hist_browser *browser,
623                                              struct callchain_node *node,
624                                              struct callchain_list *chain,
625                                              unsigned short row, u64 total,
626                                              bool need_percent, int offset,
627                                              print_callchain_entry_fn print,
628                                              struct callchain_print_arg *arg)
629 {
630         char bf[1024], *alloc_str;
631         const char *str;
632
633         if (arg->row_offset != 0) {
634                 arg->row_offset--;
635                 return 0;
636         }
637
638         alloc_str = NULL;
639         str = callchain_list__sym_name(chain, bf, sizeof(bf),
640                                        browser->show_dso);
641
642         if (need_percent) {
643                 char buf[64];
644
645                 callchain_node__scnprintf_value(node, buf, sizeof(buf),
646                                                 total);
647
648                 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
649                         str = "Not enough memory!";
650                 else
651                         str = alloc_str;
652         }
653
654         print(browser, chain, str, offset, row, arg);
655
656         free(alloc_str);
657         return 1;
658 }
659
660 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
661                                              struct rb_root *root,
662                                              unsigned short row, u64 total,
663                                              print_callchain_entry_fn print,
664                                              struct callchain_print_arg *arg,
665                                              check_output_full_fn is_output_full)
666 {
667         struct rb_node *node;
668         int first_row = row, offset = LEVEL_OFFSET_STEP;
669         bool need_percent;
670
671         node = rb_first(root);
672         need_percent = node && rb_next(node);
673
674         while (node) {
675                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
676                 struct rb_node *next = rb_next(node);
677                 struct callchain_list *chain;
678                 char folded_sign = ' ';
679                 int first = true;
680                 int extra_offset = 0;
681
682                 list_for_each_entry(chain, &child->parent_val, list) {
683                         bool was_first = first;
684
685                         if (first)
686                                 first = false;
687                         else if (need_percent)
688                                 extra_offset = LEVEL_OFFSET_STEP;
689
690                         folded_sign = callchain_list__folded(chain);
691
692                         row += hist_browser__show_callchain_list(browser, child,
693                                                         chain, row, total,
694                                                         was_first && need_percent,
695                                                         offset + extra_offset,
696                                                         print, arg);
697
698                         if (is_output_full(browser, row))
699                                 goto out;
700
701                         if (folded_sign == '+')
702                                 goto next;
703                 }
704
705                 list_for_each_entry(chain, &child->val, list) {
706                         bool was_first = first;
707
708                         if (first)
709                                 first = false;
710                         else if (need_percent)
711                                 extra_offset = LEVEL_OFFSET_STEP;
712
713                         folded_sign = callchain_list__folded(chain);
714
715                         row += hist_browser__show_callchain_list(browser, child,
716                                                         chain, row, total,
717                                                         was_first && need_percent,
718                                                         offset + extra_offset,
719                                                         print, arg);
720
721                         if (is_output_full(browser, row))
722                                 goto out;
723
724                         if (folded_sign == '+')
725                                 break;
726                 }
727
728 next:
729                 if (is_output_full(browser, row))
730                         break;
731                 node = next;
732         }
733 out:
734         return row - first_row;
735 }
736
737 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
738                                                 struct callchain_list *chain,
739                                                 char *value_str, char *old_str)
740 {
741         char bf[1024];
742         const char *str;
743         char *new;
744
745         str = callchain_list__sym_name(chain, bf, sizeof(bf),
746                                        browser->show_dso);
747         if (old_str) {
748                 if (asprintf(&new, "%s%s%s", old_str,
749                              symbol_conf.field_sep ?: ";", str) < 0)
750                         new = NULL;
751         } else {
752                 if (value_str) {
753                         if (asprintf(&new, "%s %s", value_str, str) < 0)
754                                 new = NULL;
755                 } else {
756                         if (asprintf(&new, "%s", str) < 0)
757                                 new = NULL;
758                 }
759         }
760         return new;
761 }
762
763 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
764                                                struct rb_root *root,
765                                                unsigned short row, u64 total,
766                                                print_callchain_entry_fn print,
767                                                struct callchain_print_arg *arg,
768                                                check_output_full_fn is_output_full)
769 {
770         struct rb_node *node;
771         int first_row = row, offset = LEVEL_OFFSET_STEP;
772         bool need_percent;
773
774         node = rb_first(root);
775         need_percent = node && rb_next(node);
776
777         while (node) {
778                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
779                 struct rb_node *next = rb_next(node);
780                 struct callchain_list *chain, *first_chain = NULL;
781                 int first = true;
782                 char *value_str = NULL, *value_str_alloc = NULL;
783                 char *chain_str = NULL, *chain_str_alloc = NULL;
784
785                 if (arg->row_offset != 0) {
786                         arg->row_offset--;
787                         goto next;
788                 }
789
790                 if (need_percent) {
791                         char buf[64];
792
793                         callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
794                         if (asprintf(&value_str, "%s", buf) < 0) {
795                                 value_str = (char *)"<...>";
796                                 goto do_print;
797                         }
798                         value_str_alloc = value_str;
799                 }
800
801                 list_for_each_entry(chain, &child->parent_val, list) {
802                         chain_str = hist_browser__folded_callchain_str(browser,
803                                                 chain, value_str, chain_str);
804                         if (first) {
805                                 first = false;
806                                 first_chain = chain;
807                         }
808
809                         if (chain_str == NULL) {
810                                 chain_str = (char *)"Not enough memory!";
811                                 goto do_print;
812                         }
813
814                         chain_str_alloc = chain_str;
815                 }
816
817                 list_for_each_entry(chain, &child->val, list) {
818                         chain_str = hist_browser__folded_callchain_str(browser,
819                                                 chain, value_str, chain_str);
820                         if (first) {
821                                 first = false;
822                                 first_chain = chain;
823                         }
824
825                         if (chain_str == NULL) {
826                                 chain_str = (char *)"Not enough memory!";
827                                 goto do_print;
828                         }
829
830                         chain_str_alloc = chain_str;
831                 }
832
833 do_print:
834                 print(browser, first_chain, chain_str, offset, row++, arg);
835                 free(value_str_alloc);
836                 free(chain_str_alloc);
837
838 next:
839                 if (is_output_full(browser, row))
840                         break;
841                 node = next;
842         }
843
844         return row - first_row;
845 }
846
847 static int hist_browser__show_callchain(struct hist_browser *browser,
848                                         struct rb_root *root, int level,
849                                         unsigned short row, u64 total,
850                                         print_callchain_entry_fn print,
851                                         struct callchain_print_arg *arg,
852                                         check_output_full_fn is_output_full)
853 {
854         struct rb_node *node;
855         int first_row = row, offset = level * LEVEL_OFFSET_STEP;
856         u64 new_total;
857         bool need_percent;
858
859         node = rb_first(root);
860         need_percent = node && rb_next(node);
861
862         while (node) {
863                 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
864                 struct rb_node *next = rb_next(node);
865                 struct callchain_list *chain;
866                 char folded_sign = ' ';
867                 int first = true;
868                 int extra_offset = 0;
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                 if (folded_sign == '-') {
894                         const int new_level = level + (extra_offset ? 2 : 1);
895
896                         if (callchain_param.mode == CHAIN_GRAPH_REL)
897                                 new_total = child->children_hit;
898                         else
899                                 new_total = total;
900
901                         row += hist_browser__show_callchain(browser, &child->rb_root,
902                                                             new_level, row, new_total,
903                                                             print, arg, is_output_full);
904                 }
905                 if (is_output_full(browser, row))
906                         break;
907                 node = next;
908         }
909 out:
910         return row - first_row;
911 }
912
913 struct hpp_arg {
914         struct ui_browser *b;
915         char folded_sign;
916         bool current_entry;
917 };
918
919 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
920 {
921         struct hpp_arg *arg = hpp->ptr;
922         int ret, len;
923         va_list args;
924         double percent;
925
926         va_start(args, fmt);
927         len = va_arg(args, int);
928         percent = va_arg(args, double);
929         va_end(args);
930
931         ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
932
933         ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
934         ui_browser__printf(arg->b, "%s", hpp->buf);
935
936         advance_hpp(hpp, ret);
937         return ret;
938 }
939
940 #define __HPP_COLOR_PERCENT_FN(_type, _field)                           \
941 static u64 __hpp_get_##_field(struct hist_entry *he)                    \
942 {                                                                       \
943         return he->stat._field;                                         \
944 }                                                                       \
945                                                                         \
946 static int                                                              \
947 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
948                                 struct perf_hpp *hpp,                   \
949                                 struct hist_entry *he)                  \
950 {                                                                       \
951         return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%",   \
952                         __hpp__slsmg_color_printf, true);               \
953 }
954
955 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field)                       \
956 static u64 __hpp_get_acc_##_field(struct hist_entry *he)                \
957 {                                                                       \
958         return he->stat_acc->_field;                                    \
959 }                                                                       \
960                                                                         \
961 static int                                                              \
962 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt,               \
963                                 struct perf_hpp *hpp,                   \
964                                 struct hist_entry *he)                  \
965 {                                                                       \
966         if (!symbol_conf.cumulate_callchain) {                          \
967                 struct hpp_arg *arg = hpp->ptr;                         \
968                 int len = fmt->user_len ?: fmt->len;                    \
969                 int ret = scnprintf(hpp->buf, hpp->size,                \
970                                     "%*s", len, "N/A");                 \
971                 ui_browser__printf(arg->b, "%s", hpp->buf);             \
972                                                                         \
973                 return ret;                                             \
974         }                                                               \
975         return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field,           \
976                         " %*.2f%%", __hpp__slsmg_color_printf, true);   \
977 }
978
979 __HPP_COLOR_PERCENT_FN(overhead, period)
980 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
981 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
982 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
983 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
984 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
985
986 #undef __HPP_COLOR_PERCENT_FN
987 #undef __HPP_COLOR_ACC_PERCENT_FN
988
989 void hist_browser__init_hpp(void)
990 {
991         perf_hpp__format[PERF_HPP__OVERHEAD].color =
992                                 hist_browser__hpp_color_overhead;
993         perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
994                                 hist_browser__hpp_color_overhead_sys;
995         perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
996                                 hist_browser__hpp_color_overhead_us;
997         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
998                                 hist_browser__hpp_color_overhead_guest_sys;
999         perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1000                                 hist_browser__hpp_color_overhead_guest_us;
1001         perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1002                                 hist_browser__hpp_color_overhead_acc;
1003 }
1004
1005 static int hist_browser__show_entry(struct hist_browser *browser,
1006                                     struct hist_entry *entry,
1007                                     unsigned short row)
1008 {
1009         char s[256];
1010         int printed = 0;
1011         int width = browser->b.width;
1012         char folded_sign = ' ';
1013         bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1014         off_t row_offset = entry->row_offset;
1015         bool first = true;
1016         struct perf_hpp_fmt *fmt;
1017
1018         if (current_entry) {
1019                 browser->he_selection = entry;
1020                 browser->selection = &entry->ms;
1021         }
1022
1023         if (symbol_conf.use_callchain) {
1024                 hist_entry__init_have_children(entry);
1025                 folded_sign = hist_entry__folded(entry);
1026         }
1027
1028         if (row_offset == 0) {
1029                 struct hpp_arg arg = {
1030                         .b              = &browser->b,
1031                         .folded_sign    = folded_sign,
1032                         .current_entry  = current_entry,
1033                 };
1034                 struct perf_hpp hpp = {
1035                         .buf            = s,
1036                         .size           = sizeof(s),
1037                         .ptr            = &arg,
1038                 };
1039                 int column = 0;
1040
1041                 hist_browser__gotorc(browser, row, 0);
1042
1043                 perf_hpp__for_each_format(fmt) {
1044                         if (perf_hpp__should_skip(fmt, entry->hists) ||
1045                             column++ < browser->b.horiz_scroll)
1046                                 continue;
1047
1048                         if (current_entry && browser->b.navkeypressed) {
1049                                 ui_browser__set_color(&browser->b,
1050                                                       HE_COLORSET_SELECTED);
1051                         } else {
1052                                 ui_browser__set_color(&browser->b,
1053                                                       HE_COLORSET_NORMAL);
1054                         }
1055
1056                         if (first) {
1057                                 if (symbol_conf.use_callchain) {
1058                                         ui_browser__printf(&browser->b, "%c ", folded_sign);
1059                                         width -= 2;
1060                                 }
1061                                 first = false;
1062                         } else {
1063                                 ui_browser__printf(&browser->b, "  ");
1064                                 width -= 2;
1065                         }
1066
1067                         if (fmt->color) {
1068                                 width -= fmt->color(fmt, &hpp, entry);
1069                         } else {
1070                                 width -= fmt->entry(fmt, &hpp, entry);
1071                                 ui_browser__printf(&browser->b, "%s", s);
1072                         }
1073                 }
1074
1075                 /* The scroll bar isn't being used */
1076                 if (!browser->b.navkeypressed)
1077                         width += 1;
1078
1079                 ui_browser__write_nstring(&browser->b, "", width);
1080
1081                 ++row;
1082                 ++printed;
1083         } else
1084                 --row_offset;
1085
1086         if (folded_sign == '-' && row != browser->b.rows) {
1087                 u64 total = hists__total_period(entry->hists);
1088                 struct callchain_print_arg arg = {
1089                         .row_offset = row_offset,
1090                         .is_current_entry = current_entry,
1091                 };
1092
1093                 if (callchain_param.mode == CHAIN_GRAPH_REL) {
1094                         if (symbol_conf.cumulate_callchain)
1095                                 total = entry->stat_acc->period;
1096                         else
1097                                 total = entry->stat.period;
1098                 }
1099
1100                 if (callchain_param.mode == CHAIN_FLAT) {
1101                         printed += hist_browser__show_callchain_flat(browser,
1102                                         &entry->sorted_chain, row, total,
1103                                         hist_browser__show_callchain_entry, &arg,
1104                                         hist_browser__check_output_full);
1105                 } else if (callchain_param.mode == CHAIN_FOLDED) {
1106                         printed += hist_browser__show_callchain_folded(browser,
1107                                         &entry->sorted_chain, row, total,
1108                                         hist_browser__show_callchain_entry, &arg,
1109                                         hist_browser__check_output_full);
1110                 } else {
1111                         printed += hist_browser__show_callchain(browser,
1112                                         &entry->sorted_chain, 1, row, total,
1113                                         hist_browser__show_callchain_entry, &arg,
1114                                         hist_browser__check_output_full);
1115                 }
1116
1117                 if (arg.is_current_entry)
1118                         browser->he_selection = entry;
1119         }
1120
1121         return printed;
1122 }
1123
1124 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1125 {
1126         advance_hpp(hpp, inc);
1127         return hpp->size <= 0;
1128 }
1129
1130 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1131 {
1132         struct hists *hists = browser->hists;
1133         struct perf_hpp dummy_hpp = {
1134                 .buf    = buf,
1135                 .size   = size,
1136         };
1137         struct perf_hpp_fmt *fmt;
1138         size_t ret = 0;
1139         int column = 0;
1140
1141         if (symbol_conf.use_callchain) {
1142                 ret = scnprintf(buf, size, "  ");
1143                 if (advance_hpp_check(&dummy_hpp, ret))
1144                         return ret;
1145         }
1146
1147         perf_hpp__for_each_format(fmt) {
1148                 if (perf_hpp__should_skip(fmt, hists)  || column++ < browser->b.horiz_scroll)
1149                         continue;
1150
1151                 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
1152                 if (advance_hpp_check(&dummy_hpp, ret))
1153                         break;
1154
1155                 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "  ");
1156                 if (advance_hpp_check(&dummy_hpp, ret))
1157                         break;
1158         }
1159
1160         return ret;
1161 }
1162
1163 static void hist_browser__show_headers(struct hist_browser *browser)
1164 {
1165         char headers[1024];
1166
1167         hists_browser__scnprintf_headers(browser, headers, sizeof(headers));
1168         ui_browser__gotorc(&browser->b, 0, 0);
1169         ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1170         ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1171 }
1172
1173 static void ui_browser__hists_init_top(struct ui_browser *browser)
1174 {
1175         if (browser->top == NULL) {
1176                 struct hist_browser *hb;
1177
1178                 hb = container_of(browser, struct hist_browser, b);
1179                 browser->top = rb_first(&hb->hists->entries);
1180         }
1181 }
1182
1183 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1184 {
1185         unsigned row = 0;
1186         u16 header_offset = 0;
1187         struct rb_node *nd;
1188         struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1189
1190         if (hb->show_headers) {
1191                 hist_browser__show_headers(hb);
1192                 header_offset = 1;
1193         }
1194
1195         ui_browser__hists_init_top(browser);
1196         hb->he_selection = NULL;
1197         hb->selection = NULL;
1198
1199         for (nd = browser->top; nd; nd = rb_next(nd)) {
1200                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1201                 float percent;
1202
1203                 if (h->filtered)
1204                         continue;
1205
1206                 percent = hist_entry__get_percent_limit(h);
1207                 if (percent < hb->min_pcnt)
1208                         continue;
1209
1210                 row += hist_browser__show_entry(hb, h, row);
1211                 if (row == browser->rows)
1212                         break;
1213         }
1214
1215         return row + header_offset;
1216 }
1217
1218 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1219                                              float min_pcnt)
1220 {
1221         while (nd != NULL) {
1222                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1223                 float percent = hist_entry__get_percent_limit(h);
1224
1225                 if (!h->filtered && percent >= min_pcnt)
1226                         return nd;
1227
1228                 nd = rb_next(nd);
1229         }
1230
1231         return NULL;
1232 }
1233
1234 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1235                                                   float min_pcnt)
1236 {
1237         while (nd != NULL) {
1238                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1239                 float percent = hist_entry__get_percent_limit(h);
1240
1241                 if (!h->filtered && percent >= min_pcnt)
1242                         return nd;
1243
1244                 nd = rb_prev(nd);
1245         }
1246
1247         return NULL;
1248 }
1249
1250 static void ui_browser__hists_seek(struct ui_browser *browser,
1251                                    off_t offset, int whence)
1252 {
1253         struct hist_entry *h;
1254         struct rb_node *nd;
1255         bool first = true;
1256         struct hist_browser *hb;
1257
1258         hb = container_of(browser, struct hist_browser, b);
1259
1260         if (browser->nr_entries == 0)
1261                 return;
1262
1263         ui_browser__hists_init_top(browser);
1264
1265         switch (whence) {
1266         case SEEK_SET:
1267                 nd = hists__filter_entries(rb_first(browser->entries),
1268                                            hb->min_pcnt);
1269                 break;
1270         case SEEK_CUR:
1271                 nd = browser->top;
1272                 goto do_offset;
1273         case SEEK_END:
1274                 nd = hists__filter_prev_entries(rb_last(browser->entries),
1275                                                 hb->min_pcnt);
1276                 first = false;
1277                 break;
1278         default:
1279                 return;
1280         }
1281
1282         /*
1283          * Moves not relative to the first visible entry invalidates its
1284          * row_offset:
1285          */
1286         h = rb_entry(browser->top, struct hist_entry, rb_node);
1287         h->row_offset = 0;
1288
1289         /*
1290          * Here we have to check if nd is expanded (+), if it is we can't go
1291          * the next top level hist_entry, instead we must compute an offset of
1292          * what _not_ to show and not change the first visible entry.
1293          *
1294          * This offset increments when we are going from top to bottom and
1295          * decreases when we're going from bottom to top.
1296          *
1297          * As we don't have backpointers to the top level in the callchains
1298          * structure, we need to always print the whole hist_entry callchain,
1299          * skipping the first ones that are before the first visible entry
1300          * and stop when we printed enough lines to fill the screen.
1301          */
1302 do_offset:
1303         if (!nd)
1304                 return;
1305
1306         if (offset > 0) {
1307                 do {
1308                         h = rb_entry(nd, struct hist_entry, rb_node);
1309                         if (h->unfolded) {
1310                                 u16 remaining = h->nr_rows - h->row_offset;
1311                                 if (offset > remaining) {
1312                                         offset -= remaining;
1313                                         h->row_offset = 0;
1314                                 } else {
1315                                         h->row_offset += offset;
1316                                         offset = 0;
1317                                         browser->top = nd;
1318                                         break;
1319                                 }
1320                         }
1321                         nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1322                         if (nd == NULL)
1323                                 break;
1324                         --offset;
1325                         browser->top = nd;
1326                 } while (offset != 0);
1327         } else if (offset < 0) {
1328                 while (1) {
1329                         h = rb_entry(nd, struct hist_entry, rb_node);
1330                         if (h->unfolded) {
1331                                 if (first) {
1332                                         if (-offset > h->row_offset) {
1333                                                 offset += h->row_offset;
1334                                                 h->row_offset = 0;
1335                                         } else {
1336                                                 h->row_offset += offset;
1337                                                 offset = 0;
1338                                                 browser->top = nd;
1339                                                 break;
1340                                         }
1341                                 } else {
1342                                         if (-offset > h->nr_rows) {
1343                                                 offset += h->nr_rows;
1344                                                 h->row_offset = 0;
1345                                         } else {
1346                                                 h->row_offset = h->nr_rows + offset;
1347                                                 offset = 0;
1348                                                 browser->top = nd;
1349                                                 break;
1350                                         }
1351                                 }
1352                         }
1353
1354                         nd = hists__filter_prev_entries(rb_prev(nd),
1355                                                         hb->min_pcnt);
1356                         if (nd == NULL)
1357                                 break;
1358                         ++offset;
1359                         browser->top = nd;
1360                         if (offset == 0) {
1361                                 /*
1362                                  * Last unfiltered hist_entry, check if it is
1363                                  * unfolded, if it is then we should have
1364                                  * row_offset at its last entry.
1365                                  */
1366                                 h = rb_entry(nd, struct hist_entry, rb_node);
1367                                 if (h->unfolded)
1368                                         h->row_offset = h->nr_rows;
1369                                 break;
1370                         }
1371                         first = false;
1372                 }
1373         } else {
1374                 browser->top = nd;
1375                 h = rb_entry(nd, struct hist_entry, rb_node);
1376                 h->row_offset = 0;
1377         }
1378 }
1379
1380 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1381                                            struct hist_entry *he, FILE *fp)
1382 {
1383         u64 total = hists__total_period(he->hists);
1384         struct callchain_print_arg arg  = {
1385                 .fp = fp,
1386         };
1387
1388         if (symbol_conf.cumulate_callchain)
1389                 total = he->stat_acc->period;
1390
1391         hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1392                                      hist_browser__fprintf_callchain_entry, &arg,
1393                                      hist_browser__check_dump_full);
1394         return arg.printed;
1395 }
1396
1397 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1398                                        struct hist_entry *he, FILE *fp)
1399 {
1400         char s[8192];
1401         int printed = 0;
1402         char folded_sign = ' ';
1403         struct perf_hpp hpp = {
1404                 .buf = s,
1405                 .size = sizeof(s),
1406         };
1407         struct perf_hpp_fmt *fmt;
1408         bool first = true;
1409         int ret;
1410
1411         if (symbol_conf.use_callchain)
1412                 folded_sign = hist_entry__folded(he);
1413
1414         if (symbol_conf.use_callchain)
1415                 printed += fprintf(fp, "%c ", folded_sign);
1416
1417         perf_hpp__for_each_format(fmt) {
1418                 if (perf_hpp__should_skip(fmt, he->hists))
1419                         continue;
1420
1421                 if (!first) {
1422                         ret = scnprintf(hpp.buf, hpp.size, "  ");
1423                         advance_hpp(&hpp, ret);
1424                 } else
1425                         first = false;
1426
1427                 ret = fmt->entry(fmt, &hpp, he);
1428                 advance_hpp(&hpp, ret);
1429         }
1430         printed += fprintf(fp, "%s\n", rtrim(s));
1431
1432         if (folded_sign == '-')
1433                 printed += hist_browser__fprintf_callchain(browser, he, fp);
1434
1435         return printed;
1436 }
1437
1438 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1439 {
1440         struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1441                                                    browser->min_pcnt);
1442         int printed = 0;
1443
1444         while (nd) {
1445                 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1446
1447                 printed += hist_browser__fprintf_entry(browser, h, fp);
1448                 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1449         }
1450
1451         return printed;
1452 }
1453
1454 static int hist_browser__dump(struct hist_browser *browser)
1455 {
1456         char filename[64];
1457         FILE *fp;
1458
1459         while (1) {
1460                 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1461                 if (access(filename, F_OK))
1462                         break;
1463                 /*
1464                  * XXX: Just an arbitrary lazy upper limit
1465                  */
1466                 if (++browser->print_seq == 8192) {
1467                         ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1468                         return -1;
1469                 }
1470         }
1471
1472         fp = fopen(filename, "w");
1473         if (fp == NULL) {
1474                 char bf[64];
1475                 const char *err = strerror_r(errno, bf, sizeof(bf));
1476                 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1477                 return -1;
1478         }
1479
1480         ++browser->print_seq;
1481         hist_browser__fprintf(browser, fp);
1482         fclose(fp);
1483         ui_helpline__fpush("%s written!", filename);
1484
1485         return 0;
1486 }
1487
1488 static struct hist_browser *hist_browser__new(struct hists *hists,
1489                                               struct hist_browser_timer *hbt,
1490                                               struct perf_env *env)
1491 {
1492         struct hist_browser *browser = zalloc(sizeof(*browser));
1493
1494         if (browser) {
1495                 browser->hists = hists;
1496                 browser->b.refresh = hist_browser__refresh;
1497                 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1498                 browser->b.seek = ui_browser__hists_seek;
1499                 browser->b.use_navkeypressed = true;
1500                 browser->show_headers = symbol_conf.show_hist_headers;
1501                 browser->hbt = hbt;
1502                 browser->env = env;
1503         }
1504
1505         return browser;
1506 }
1507
1508 static void hist_browser__delete(struct hist_browser *browser)
1509 {
1510         free(browser);
1511 }
1512
1513 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1514 {
1515         return browser->he_selection;
1516 }
1517
1518 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1519 {
1520         return browser->he_selection->thread;
1521 }
1522
1523 /* Check whether the browser is for 'top' or 'report' */
1524 static inline bool is_report_browser(void *timer)
1525 {
1526         return timer == NULL;
1527 }
1528
1529 static int hists__browser_title(struct hists *hists,
1530                                 struct hist_browser_timer *hbt,
1531                                 char *bf, size_t size)
1532 {
1533         char unit;
1534         int printed;
1535         const struct dso *dso = hists->dso_filter;
1536         const struct thread *thread = hists->thread_filter;
1537         int socket_id = hists->socket_filter;
1538         unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1539         u64 nr_events = hists->stats.total_period;
1540         struct perf_evsel *evsel = hists_to_evsel(hists);
1541         const char *ev_name = perf_evsel__name(evsel);
1542         char buf[512];
1543         size_t buflen = sizeof(buf);
1544         char ref[30] = " show reference callgraph, ";
1545         bool enable_ref = false;
1546
1547         if (symbol_conf.filter_relative) {
1548                 nr_samples = hists->stats.nr_non_filtered_samples;
1549                 nr_events = hists->stats.total_non_filtered_period;
1550         }
1551
1552         if (perf_evsel__is_group_event(evsel)) {
1553                 struct perf_evsel *pos;
1554
1555                 perf_evsel__group_desc(evsel, buf, buflen);
1556                 ev_name = buf;
1557
1558                 for_each_group_member(pos, evsel) {
1559                         struct hists *pos_hists = evsel__hists(pos);
1560
1561                         if (symbol_conf.filter_relative) {
1562                                 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1563                                 nr_events += pos_hists->stats.total_non_filtered_period;
1564                         } else {
1565                                 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1566                                 nr_events += pos_hists->stats.total_period;
1567                         }
1568                 }
1569         }
1570
1571         if (symbol_conf.show_ref_callgraph &&
1572             strstr(ev_name, "call-graph=no"))
1573                 enable_ref = true;
1574         nr_samples = convert_unit(nr_samples, &unit);
1575         printed = scnprintf(bf, size,
1576                            "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
1577                            nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
1578
1579
1580         if (hists->uid_filter_str)
1581                 printed += snprintf(bf + printed, size - printed,
1582                                     ", UID: %s", hists->uid_filter_str);
1583         if (thread)
1584                 printed += scnprintf(bf + printed, size - printed,
1585                                     ", Thread: %s(%d)",
1586                                      (thread->comm_set ? thread__comm_str(thread) : ""),
1587                                     thread->tid);
1588         if (dso)
1589                 printed += scnprintf(bf + printed, size - printed,
1590                                     ", DSO: %s", dso->short_name);
1591         if (socket_id > -1)
1592                 printed += scnprintf(bf + printed, size - printed,
1593                                     ", Processor Socket: %d", socket_id);
1594         if (!is_report_browser(hbt)) {
1595                 struct perf_top *top = hbt->arg;
1596
1597                 if (top->zero)
1598                         printed += scnprintf(bf + printed, size - printed, " [z]");
1599         }
1600
1601         return printed;
1602 }
1603
1604 static inline void free_popup_options(char **options, int n)
1605 {
1606         int i;
1607
1608         for (i = 0; i < n; ++i)
1609                 zfree(&options[i]);
1610 }
1611
1612 /*
1613  * Only runtime switching of perf data file will make "input_name" point
1614  * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1615  * whether we need to call free() for current "input_name" during the switch.
1616  */
1617 static bool is_input_name_malloced = false;
1618
1619 static int switch_data_file(void)
1620 {
1621         char *pwd, *options[32], *abs_path[32], *tmp;
1622         DIR *pwd_dir;
1623         int nr_options = 0, choice = -1, ret = -1;
1624         struct dirent *dent;
1625
1626         pwd = getenv("PWD");
1627         if (!pwd)
1628                 return ret;
1629
1630         pwd_dir = opendir(pwd);
1631         if (!pwd_dir)
1632                 return ret;
1633
1634         memset(options, 0, sizeof(options));
1635         memset(options, 0, sizeof(abs_path));
1636
1637         while ((dent = readdir(pwd_dir))) {
1638                 char path[PATH_MAX];
1639                 u64 magic;
1640                 char *name = dent->d_name;
1641                 FILE *file;
1642
1643                 if (!(dent->d_type == DT_REG))
1644                         continue;
1645
1646                 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1647
1648                 file = fopen(path, "r");
1649                 if (!file)
1650                         continue;
1651
1652                 if (fread(&magic, 1, 8, file) < 8)
1653                         goto close_file_and_continue;
1654
1655                 if (is_perf_magic(magic)) {
1656                         options[nr_options] = strdup(name);
1657                         if (!options[nr_options])
1658                                 goto close_file_and_continue;
1659
1660                         abs_path[nr_options] = strdup(path);
1661                         if (!abs_path[nr_options]) {
1662                                 zfree(&options[nr_options]);
1663                                 ui__warning("Can't search all data files due to memory shortage.\n");
1664                                 fclose(file);
1665                                 break;
1666                         }
1667
1668                         nr_options++;
1669                 }
1670
1671 close_file_and_continue:
1672                 fclose(file);
1673                 if (nr_options >= 32) {
1674                         ui__warning("Too many perf data files in PWD!\n"
1675                                     "Only the first 32 files will be listed.\n");
1676                         break;
1677                 }
1678         }
1679         closedir(pwd_dir);
1680
1681         if (nr_options) {
1682                 choice = ui__popup_menu(nr_options, options);
1683                 if (choice < nr_options && choice >= 0) {
1684                         tmp = strdup(abs_path[choice]);
1685                         if (tmp) {
1686                                 if (is_input_name_malloced)
1687                                         free((void *)input_name);
1688                                 input_name = tmp;
1689                                 is_input_name_malloced = true;
1690                                 ret = 0;
1691                         } else
1692                                 ui__warning("Data switch failed due to memory shortage!\n");
1693                 }
1694         }
1695
1696         free_popup_options(options, nr_options);
1697         free_popup_options(abs_path, nr_options);
1698         return ret;
1699 }
1700
1701 struct popup_action {
1702         struct thread           *thread;
1703         struct map_symbol       ms;
1704         int                     socket;
1705
1706         int (*fn)(struct hist_browser *browser, struct popup_action *act);
1707 };
1708
1709 static int
1710 do_annotate(struct hist_browser *browser, struct popup_action *act)
1711 {
1712         struct perf_evsel *evsel;
1713         struct annotation *notes;
1714         struct hist_entry *he;
1715         int err;
1716
1717         if (!objdump_path && perf_env__lookup_objdump(browser->env))
1718                 return 0;
1719
1720         notes = symbol__annotation(act->ms.sym);
1721         if (!notes->src)
1722                 return 0;
1723
1724         evsel = hists_to_evsel(browser->hists);
1725         err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
1726         he = hist_browser__selected_entry(browser);
1727         /*
1728          * offer option to annotate the other branch source or target
1729          * (if they exists) when returning from annotate
1730          */
1731         if ((err == 'q' || err == CTRL('c')) && he->branch_info)
1732                 return 1;
1733
1734         ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1735         if (err)
1736                 ui_browser__handle_resize(&browser->b);
1737         return 0;
1738 }
1739
1740 static int
1741 add_annotate_opt(struct hist_browser *browser __maybe_unused,
1742                  struct popup_action *act, char **optstr,
1743                  struct map *map, struct symbol *sym)
1744 {
1745         if (sym == NULL || map->dso->annotate_warned)
1746                 return 0;
1747
1748         if (asprintf(optstr, "Annotate %s", sym->name) < 0)
1749                 return 0;
1750
1751         act->ms.map = map;
1752         act->ms.sym = sym;
1753         act->fn = do_annotate;
1754         return 1;
1755 }
1756
1757 static int
1758 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
1759 {
1760         struct thread *thread = act->thread;
1761
1762         if (browser->hists->thread_filter) {
1763                 pstack__remove(browser->pstack, &browser->hists->thread_filter);
1764                 perf_hpp__set_elide(HISTC_THREAD, false);
1765                 thread__zput(browser->hists->thread_filter);
1766                 ui_helpline__pop();
1767         } else {
1768                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
1769                                    thread->comm_set ? thread__comm_str(thread) : "",
1770                                    thread->tid);
1771                 browser->hists->thread_filter = thread__get(thread);
1772                 perf_hpp__set_elide(HISTC_THREAD, false);
1773                 pstack__push(browser->pstack, &browser->hists->thread_filter);
1774         }
1775
1776         hists__filter_by_thread(browser->hists);
1777         hist_browser__reset(browser);
1778         return 0;
1779 }
1780
1781 static int
1782 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
1783                char **optstr, struct thread *thread)
1784 {
1785         if (thread == NULL)
1786                 return 0;
1787
1788         if (asprintf(optstr, "Zoom %s %s(%d) thread",
1789                      browser->hists->thread_filter ? "out of" : "into",
1790                      thread->comm_set ? thread__comm_str(thread) : "",
1791                      thread->tid) < 0)
1792                 return 0;
1793
1794         act->thread = thread;
1795         act->fn = do_zoom_thread;
1796         return 1;
1797 }
1798
1799 static int
1800 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
1801 {
1802         struct map *map = act->ms.map;
1803
1804         if (browser->hists->dso_filter) {
1805                 pstack__remove(browser->pstack, &browser->hists->dso_filter);
1806                 perf_hpp__set_elide(HISTC_DSO, false);
1807                 browser->hists->dso_filter = NULL;
1808                 ui_helpline__pop();
1809         } else {
1810                 if (map == NULL)
1811                         return 0;
1812                 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
1813                                    __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
1814                 browser->hists->dso_filter = map->dso;
1815                 perf_hpp__set_elide(HISTC_DSO, true);
1816                 pstack__push(browser->pstack, &browser->hists->dso_filter);
1817         }
1818
1819         hists__filter_by_dso(browser->hists);
1820         hist_browser__reset(browser);
1821         return 0;
1822 }
1823
1824 static int
1825 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
1826             char **optstr, struct map *map)
1827 {
1828         if (map == NULL)
1829                 return 0;
1830
1831         if (asprintf(optstr, "Zoom %s %s DSO",
1832                      browser->hists->dso_filter ? "out of" : "into",
1833                      __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
1834                 return 0;
1835
1836         act->ms.map = map;
1837         act->fn = do_zoom_dso;
1838         return 1;
1839 }
1840
1841 static int
1842 do_browse_map(struct hist_browser *browser __maybe_unused,
1843               struct popup_action *act)
1844 {
1845         map__browse(act->ms.map);
1846         return 0;
1847 }
1848
1849 static int
1850 add_map_opt(struct hist_browser *browser __maybe_unused,
1851             struct popup_action *act, char **optstr, struct map *map)
1852 {
1853         if (map == NULL)
1854                 return 0;
1855
1856         if (asprintf(optstr, "Browse map details") < 0)
1857                 return 0;
1858
1859         act->ms.map = map;
1860         act->fn = do_browse_map;
1861         return 1;
1862 }
1863
1864 static int
1865 do_run_script(struct hist_browser *browser __maybe_unused,
1866               struct popup_action *act)
1867 {
1868         char script_opt[64];
1869         memset(script_opt, 0, sizeof(script_opt));
1870
1871         if (act->thread) {
1872                 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
1873                           thread__comm_str(act->thread));
1874         } else if (act->ms.sym) {
1875                 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
1876                           act->ms.sym->name);
1877         }
1878
1879         script_browse(script_opt);
1880         return 0;
1881 }
1882
1883 static int
1884 add_script_opt(struct hist_browser *browser __maybe_unused,
1885                struct popup_action *act, char **optstr,
1886                struct thread *thread, struct symbol *sym)
1887 {
1888         if (thread) {
1889                 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
1890                              thread__comm_str(thread)) < 0)
1891                         return 0;
1892         } else if (sym) {
1893                 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
1894                              sym->name) < 0)
1895                         return 0;
1896         } else {
1897                 if (asprintf(optstr, "Run scripts for all samples") < 0)
1898                         return 0;
1899         }
1900
1901         act->thread = thread;
1902         act->ms.sym = sym;
1903         act->fn = do_run_script;
1904         return 1;
1905 }
1906
1907 static int
1908 do_switch_data(struct hist_browser *browser __maybe_unused,
1909                struct popup_action *act __maybe_unused)
1910 {
1911         if (switch_data_file()) {
1912                 ui__warning("Won't switch the data files due to\n"
1913                             "no valid data file get selected!\n");
1914                 return 0;
1915         }
1916
1917         return K_SWITCH_INPUT_DATA;
1918 }
1919
1920 static int
1921 add_switch_opt(struct hist_browser *browser,
1922                struct popup_action *act, char **optstr)
1923 {
1924         if (!is_report_browser(browser->hbt))
1925                 return 0;
1926
1927         if (asprintf(optstr, "Switch to another data file in PWD") < 0)
1928                 return 0;
1929
1930         act->fn = do_switch_data;
1931         return 1;
1932 }
1933
1934 static int
1935 do_exit_browser(struct hist_browser *browser __maybe_unused,
1936                 struct popup_action *act __maybe_unused)
1937 {
1938         return 0;
1939 }
1940
1941 static int
1942 add_exit_opt(struct hist_browser *browser __maybe_unused,
1943              struct popup_action *act, char **optstr)
1944 {
1945         if (asprintf(optstr, "Exit") < 0)
1946                 return 0;
1947
1948         act->fn = do_exit_browser;
1949         return 1;
1950 }
1951
1952 static int
1953 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
1954 {
1955         if (browser->hists->socket_filter > -1) {
1956                 pstack__remove(browser->pstack, &browser->hists->socket_filter);
1957                 browser->hists->socket_filter = -1;
1958                 perf_hpp__set_elide(HISTC_SOCKET, false);
1959         } else {
1960                 browser->hists->socket_filter = act->socket;
1961                 perf_hpp__set_elide(HISTC_SOCKET, true);
1962                 pstack__push(browser->pstack, &browser->hists->socket_filter);
1963         }
1964
1965         hists__filter_by_socket(browser->hists);
1966         hist_browser__reset(browser);
1967         return 0;
1968 }
1969
1970 static int
1971 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
1972                char **optstr, int socket_id)
1973 {
1974         if (socket_id < 0)
1975                 return 0;
1976
1977         if (asprintf(optstr, "Zoom %s Processor Socket %d",
1978                      (browser->hists->socket_filter > -1) ? "out of" : "into",
1979                      socket_id) < 0)
1980                 return 0;
1981
1982         act->socket = socket_id;
1983         act->fn = do_zoom_socket;
1984         return 1;
1985 }
1986
1987 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1988 {
1989         u64 nr_entries = 0;
1990         struct rb_node *nd = rb_first(&hb->hists->entries);
1991
1992         if (hb->min_pcnt == 0) {
1993                 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1994                 return;
1995         }
1996
1997         while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1998                 nr_entries++;
1999                 nd = rb_next(nd);
2000         }
2001
2002         hb->nr_non_filtered_entries = nr_entries;
2003 }
2004
2005 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2006                                     const char *helpline,
2007                                     bool left_exits,
2008                                     struct hist_browser_timer *hbt,
2009                                     float min_pcnt,
2010                                     struct perf_env *env)
2011 {
2012         struct hists *hists = evsel__hists(evsel);
2013         struct hist_browser *browser = hist_browser__new(hists, hbt, env);
2014         struct branch_info *bi;
2015 #define MAX_OPTIONS  16
2016         char *options[MAX_OPTIONS];
2017         struct popup_action actions[MAX_OPTIONS];
2018         int nr_options = 0;
2019         int key = -1;
2020         char buf[64];
2021         int delay_secs = hbt ? hbt->refresh : 0;
2022         struct perf_hpp_fmt *fmt;
2023
2024 #define HIST_BROWSER_HELP_COMMON                                        \
2025         "h/?/F1        Show this window\n"                              \
2026         "UP/DOWN/PGUP\n"                                                \
2027         "PGDN/SPACE    Navigate\n"                                      \
2028         "q/ESC/CTRL+C  Exit browser\n\n"                                \
2029         "For multiple event sessions:\n\n"                              \
2030         "TAB/UNTAB     Switch events\n\n"                               \
2031         "For symbolic views (--sort has sym):\n\n"                      \
2032         "ENTER         Zoom into DSO/Threads & Annotate current symbol\n" \
2033         "ESC           Zoom out\n"                                      \
2034         "a             Annotate current symbol\n"                       \
2035         "C             Collapse all callchains\n"                       \
2036         "d             Zoom into current DSO\n"                         \
2037         "E             Expand all callchains\n"                         \
2038         "F             Toggle percentage of filtered entries\n"         \
2039         "H             Display column headers\n"                        \
2040         "m             Display context menu\n"                          \
2041         "S             Zoom into current Processor Socket\n"            \
2042
2043         /* help messages are sorted by lexical order of the hotkey */
2044         const char report_help[] = HIST_BROWSER_HELP_COMMON
2045         "i             Show header information\n"
2046         "P             Print histograms to perf.hist.N\n"
2047         "r             Run available scripts\n"
2048         "s             Switch to another data file in PWD\n"
2049         "t             Zoom into current Thread\n"
2050         "V             Verbose (DSO names in callchains, etc)\n"
2051         "/             Filter symbol by name";
2052         const char top_help[] = HIST_BROWSER_HELP_COMMON
2053         "P             Print histograms to perf.hist.N\n"
2054         "t             Zoom into current Thread\n"
2055         "V             Verbose (DSO names in callchains, etc)\n"
2056         "z             Toggle zeroing of samples\n"
2057         "f             Enable/Disable events\n"
2058         "/             Filter symbol by name";
2059
2060         if (browser == NULL)
2061                 return -1;
2062
2063         /* reset abort key so that it can get Ctrl-C as a key */
2064         SLang_reset_tty();
2065         SLang_init_tty(0, 0, 0);
2066
2067         if (min_pcnt)
2068                 browser->min_pcnt = min_pcnt;
2069         hist_browser__update_nr_entries(browser);
2070
2071         browser->pstack = pstack__new(3);
2072         if (browser->pstack == NULL)
2073                 goto out;
2074
2075         ui_helpline__push(helpline);
2076
2077         memset(options, 0, sizeof(options));
2078         memset(actions, 0, sizeof(actions));
2079
2080         perf_hpp__for_each_format(fmt) {
2081                 perf_hpp__reset_width(fmt, hists);
2082                 /*
2083                  * This is done just once, and activates the horizontal scrolling
2084                  * code in the ui_browser code, it would be better to have a the
2085                  * counter in the perf_hpp code, but I couldn't find doing it here
2086                  * works, FIXME by setting this in hist_browser__new, for now, be
2087                  * clever 8-)
2088                  */
2089                 ++browser->b.columns;
2090         }
2091
2092         if (symbol_conf.col_width_list_str)
2093                 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2094
2095         while (1) {
2096                 struct thread *thread = NULL;
2097                 struct map *map = NULL;
2098                 int choice = 0;
2099                 int socked_id = -1;
2100
2101                 nr_options = 0;
2102
2103                 key = hist_browser__run(browser, helpline);
2104
2105                 if (browser->he_selection != NULL) {
2106                         thread = hist_browser__selected_thread(browser);
2107                         map = browser->selection->map;
2108                         socked_id = browser->he_selection->socket;
2109                 }
2110                 switch (key) {
2111                 case K_TAB:
2112                 case K_UNTAB:
2113                         if (nr_events == 1)
2114                                 continue;
2115                         /*
2116                          * Exit the browser, let hists__browser_tree
2117                          * go to the next or previous
2118                          */
2119                         goto out_free_stack;
2120                 case 'a':
2121                         if (!sort__has_sym) {
2122                                 ui_browser__warning(&browser->b, delay_secs * 2,
2123                         "Annotation is only available for symbolic views, "
2124                         "include \"sym*\" in --sort to use it.");
2125                                 continue;
2126                         }
2127
2128                         if (browser->selection == NULL ||
2129                             browser->selection->sym == NULL ||
2130                             browser->selection->map->dso->annotate_warned)
2131                                 continue;
2132
2133                         actions->ms.map = browser->selection->map;
2134                         actions->ms.sym = browser->selection->sym;
2135                         do_annotate(browser, actions);
2136                         continue;
2137                 case 'P':
2138                         hist_browser__dump(browser);
2139                         continue;
2140                 case 'd':
2141                         actions->ms.map = map;
2142                         do_zoom_dso(browser, actions);
2143                         continue;
2144                 case 'V':
2145                         browser->show_dso = !browser->show_dso;
2146                         continue;
2147                 case 't':
2148                         actions->thread = thread;
2149                         do_zoom_thread(browser, actions);
2150                         continue;
2151                 case 'S':
2152                         actions->socket = socked_id;
2153                         do_zoom_socket(browser, actions);
2154                         continue;
2155                 case '/':
2156                         if (ui_browser__input_window("Symbol to show",
2157                                         "Please enter the name of symbol you want to see.\n"
2158                                         "To remove the filter later, press / + ENTER.",
2159                                         buf, "ENTER: OK, ESC: Cancel",
2160                                         delay_secs * 2) == K_ENTER) {
2161                                 hists->symbol_filter_str = *buf ? buf : NULL;
2162                                 hists__filter_by_symbol(hists);
2163                                 hist_browser__reset(browser);
2164                         }
2165                         continue;
2166                 case 'r':
2167                         if (is_report_browser(hbt)) {
2168                                 actions->thread = NULL;
2169                                 actions->ms.sym = NULL;
2170                                 do_run_script(browser, actions);
2171                         }
2172                         continue;
2173                 case 's':
2174                         if (is_report_browser(hbt)) {
2175                                 key = do_switch_data(browser, actions);
2176                                 if (key == K_SWITCH_INPUT_DATA)
2177                                         goto out_free_stack;
2178                         }
2179                         continue;
2180                 case 'i':
2181                         /* env->arch is NULL for live-mode (i.e. perf top) */
2182                         if (env->arch)
2183                                 tui__header_window(env);
2184                         continue;
2185                 case 'F':
2186                         symbol_conf.filter_relative ^= 1;
2187                         continue;
2188                 case 'z':
2189                         if (!is_report_browser(hbt)) {
2190                                 struct perf_top *top = hbt->arg;
2191
2192                                 top->zero = !top->zero;
2193                         }
2194                         continue;
2195                 case K_F1:
2196                 case 'h':
2197                 case '?':
2198                         ui_browser__help_window(&browser->b,
2199                                 is_report_browser(hbt) ? report_help : top_help);
2200                         continue;
2201                 case K_ENTER:
2202                 case K_RIGHT:
2203                 case 'm':
2204                         /* menu */
2205                         break;
2206                 case K_ESC:
2207                 case K_LEFT: {
2208                         const void *top;
2209
2210                         if (pstack__empty(browser->pstack)) {
2211                                 /*
2212                                  * Go back to the perf_evsel_menu__run or other user
2213                                  */
2214                                 if (left_exits)
2215                                         goto out_free_stack;
2216
2217                                 if (key == K_ESC &&
2218                                     ui_browser__dialog_yesno(&browser->b,
2219                                                              "Do you really want to exit?"))
2220                                         goto out_free_stack;
2221
2222                                 continue;
2223                         }
2224                         top = pstack__peek(browser->pstack);
2225                         if (top == &browser->hists->dso_filter) {
2226                                 /*
2227                                  * No need to set actions->dso here since
2228                                  * it's just to remove the current filter.
2229                                  * Ditto for thread below.
2230                                  */
2231                                 do_zoom_dso(browser, actions);
2232                         } else if (top == &browser->hists->thread_filter) {
2233                                 do_zoom_thread(browser, actions);
2234                         } else if (top == &browser->hists->socket_filter) {
2235                                 do_zoom_socket(browser, actions);
2236                         }
2237                         continue;
2238                 }
2239                 case 'q':
2240                 case CTRL('c'):
2241                         goto out_free_stack;
2242                 case 'f':
2243                         if (!is_report_browser(hbt)) {
2244                                 struct perf_top *top = hbt->arg;
2245
2246                                 perf_evlist__toggle_enable(top->evlist);
2247                                 /*
2248                                  * No need to refresh, resort/decay histogram
2249                                  * entries if we are not collecting samples:
2250                                  */
2251                                 if (top->evlist->enabled) {
2252                                         helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2253                                         hbt->refresh = delay_secs;
2254                                 } else {
2255                                         helpline = "Press 'f' again to re-enable the events";
2256                                         hbt->refresh = 0;
2257                                 }
2258                                 continue;
2259                         }
2260                         /* Fall thru */
2261                 default:
2262                         helpline = "Press '?' for help on key bindings";
2263                         continue;
2264                 }
2265
2266                 if (!sort__has_sym)
2267                         goto add_exit_option;
2268
2269                 if (browser->selection == NULL)
2270                         goto skip_annotation;
2271
2272                 if (sort__mode == SORT_MODE__BRANCH) {
2273                         bi = browser->he_selection->branch_info;
2274
2275                         if (bi == NULL)
2276                                 goto skip_annotation;
2277
2278                         nr_options += add_annotate_opt(browser,
2279                                                        &actions[nr_options],
2280                                                        &options[nr_options],
2281                                                        bi->from.map,
2282                                                        bi->from.sym);
2283                         if (bi->to.sym != bi->from.sym)
2284                                 nr_options += add_annotate_opt(browser,
2285                                                         &actions[nr_options],
2286                                                         &options[nr_options],
2287                                                         bi->to.map,
2288                                                         bi->to.sym);
2289                 } else {
2290                         nr_options += add_annotate_opt(browser,
2291                                                        &actions[nr_options],
2292                                                        &options[nr_options],
2293                                                        browser->selection->map,
2294                                                        browser->selection->sym);
2295                 }
2296 skip_annotation:
2297                 nr_options += add_thread_opt(browser, &actions[nr_options],
2298                                              &options[nr_options], thread);
2299                 nr_options += add_dso_opt(browser, &actions[nr_options],
2300                                           &options[nr_options], map);
2301                 nr_options += add_map_opt(browser, &actions[nr_options],
2302                                           &options[nr_options],
2303                                           browser->selection ?
2304                                                 browser->selection->map : NULL);
2305                 nr_options += add_socket_opt(browser, &actions[nr_options],
2306                                              &options[nr_options],
2307                                              socked_id);
2308                 /* perf script support */
2309                 if (browser->he_selection) {
2310                         nr_options += add_script_opt(browser,
2311                                                      &actions[nr_options],
2312                                                      &options[nr_options],
2313                                                      thread, NULL);
2314                         /*
2315                          * Note that browser->selection != NULL
2316                          * when browser->he_selection is not NULL,
2317                          * so we don't need to check browser->selection
2318                          * before fetching browser->selection->sym like what
2319                          * we do before fetching browser->selection->map.
2320                          *
2321                          * See hist_browser__show_entry.
2322                          */
2323                         nr_options += add_script_opt(browser,
2324                                                      &actions[nr_options],
2325                                                      &options[nr_options],
2326                                                      NULL, browser->selection->sym);
2327                 }
2328                 nr_options += add_script_opt(browser, &actions[nr_options],
2329                                              &options[nr_options], NULL, NULL);
2330                 nr_options += add_switch_opt(browser, &actions[nr_options],
2331                                              &options[nr_options]);
2332 add_exit_option:
2333                 nr_options += add_exit_opt(browser, &actions[nr_options],
2334                                            &options[nr_options]);
2335
2336                 do {
2337                         struct popup_action *act;
2338
2339                         choice = ui__popup_menu(nr_options, options);
2340                         if (choice == -1 || choice >= nr_options)
2341                                 break;
2342
2343                         act = &actions[choice];
2344                         key = act->fn(browser, act);
2345                 } while (key == 1);
2346
2347                 if (key == K_SWITCH_INPUT_DATA)
2348                         break;
2349         }
2350 out_free_stack:
2351         pstack__delete(browser->pstack);
2352 out:
2353         hist_browser__delete(browser);
2354         free_popup_options(options, MAX_OPTIONS);
2355         return key;
2356 }
2357
2358 struct perf_evsel_menu {
2359         struct ui_browser b;
2360         struct perf_evsel *selection;
2361         bool lost_events, lost_events_warned;
2362         float min_pcnt;
2363         struct perf_env *env;
2364 };
2365
2366 static void perf_evsel_menu__write(struct ui_browser *browser,
2367                                    void *entry, int row)
2368 {
2369         struct perf_evsel_menu *menu = container_of(browser,
2370                                                     struct perf_evsel_menu, b);
2371         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2372         struct hists *hists = evsel__hists(evsel);
2373         bool current_entry = ui_browser__is_current_entry(browser, row);
2374         unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2375         const char *ev_name = perf_evsel__name(evsel);
2376         char bf[256], unit;
2377         const char *warn = " ";
2378         size_t printed;
2379
2380         ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
2381                                                        HE_COLORSET_NORMAL);
2382
2383         if (perf_evsel__is_group_event(evsel)) {
2384                 struct perf_evsel *pos;
2385
2386                 ev_name = perf_evsel__group_name(evsel);
2387
2388                 for_each_group_member(pos, evsel) {
2389                         struct hists *pos_hists = evsel__hists(pos);
2390                         nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2391                 }
2392         }
2393
2394         nr_events = convert_unit(nr_events, &unit);
2395         printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
2396                            unit, unit == ' ' ? "" : " ", ev_name);
2397         ui_browser__printf(browser, "%s", bf);
2398
2399         nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
2400         if (nr_events != 0) {
2401                 menu->lost_events = true;
2402                 if (!current_entry)
2403                         ui_browser__set_color(browser, HE_COLORSET_TOP);
2404                 nr_events = convert_unit(nr_events, &unit);
2405                 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
2406                                      nr_events, unit, unit == ' ' ? "" : " ");
2407                 warn = bf;
2408         }
2409
2410         ui_browser__write_nstring(browser, warn, browser->width - printed);
2411
2412         if (current_entry)
2413                 menu->selection = evsel;
2414 }
2415
2416 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
2417                                 int nr_events, const char *help,
2418                                 struct hist_browser_timer *hbt)
2419 {
2420         struct perf_evlist *evlist = menu->b.priv;
2421         struct perf_evsel *pos;
2422         const char *title = "Available samples";
2423         int delay_secs = hbt ? hbt->refresh : 0;
2424         int key;
2425
2426         if (ui_browser__show(&menu->b, title,
2427                              "ESC: exit, ENTER|->: Browse histograms") < 0)
2428                 return -1;
2429
2430         while (1) {
2431                 key = ui_browser__run(&menu->b, delay_secs);
2432
2433                 switch (key) {
2434                 case K_TIMER:
2435                         hbt->timer(hbt->arg);
2436
2437                         if (!menu->lost_events_warned && menu->lost_events) {
2438                                 ui_browser__warn_lost_events(&menu->b);
2439                                 menu->lost_events_warned = true;
2440                         }
2441                         continue;
2442                 case K_RIGHT:
2443                 case K_ENTER:
2444                         if (!menu->selection)
2445                                 continue;
2446                         pos = menu->selection;
2447 browse_hists:
2448                         perf_evlist__set_selected(evlist, pos);
2449                         /*
2450                          * Give the calling tool a chance to populate the non
2451                          * default evsel resorted hists tree.
2452                          */
2453                         if (hbt)
2454                                 hbt->timer(hbt->arg);
2455                         key = perf_evsel__hists_browse(pos, nr_events, help,
2456                                                        true, hbt,
2457                                                        menu->min_pcnt,
2458                                                        menu->env);
2459                         ui_browser__show_title(&menu->b, title);
2460                         switch (key) {
2461                         case K_TAB:
2462                                 if (pos->node.next == &evlist->entries)
2463                                         pos = perf_evlist__first(evlist);
2464                                 else
2465                                         pos = perf_evsel__next(pos);
2466                                 goto browse_hists;
2467                         case K_UNTAB:
2468                                 if (pos->node.prev == &evlist->entries)
2469                                         pos = perf_evlist__last(evlist);
2470                                 else
2471                                         pos = perf_evsel__prev(pos);
2472                                 goto browse_hists;
2473                         case K_SWITCH_INPUT_DATA:
2474                         case 'q':
2475                         case CTRL('c'):
2476                                 goto out;
2477                         case K_ESC:
2478                         default:
2479                                 continue;
2480                         }
2481                 case K_LEFT:
2482                         continue;
2483                 case K_ESC:
2484                         if (!ui_browser__dialog_yesno(&menu->b,
2485                                                "Do you really want to exit?"))
2486                                 continue;
2487                         /* Fall thru */
2488                 case 'q':
2489                 case CTRL('c'):
2490                         goto out;
2491                 default:
2492                         continue;
2493                 }
2494         }
2495
2496 out:
2497         ui_browser__hide(&menu->b);
2498         return key;
2499 }
2500
2501 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
2502                                  void *entry)
2503 {
2504         struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
2505
2506         if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
2507                 return true;
2508
2509         return false;
2510 }
2511
2512 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
2513                                            int nr_entries, const char *help,
2514                                            struct hist_browser_timer *hbt,
2515                                            float min_pcnt,
2516                                            struct perf_env *env)
2517 {
2518         struct perf_evsel *pos;
2519         struct perf_evsel_menu menu = {
2520                 .b = {
2521                         .entries    = &evlist->entries,
2522                         .refresh    = ui_browser__list_head_refresh,
2523                         .seek       = ui_browser__list_head_seek,
2524                         .write      = perf_evsel_menu__write,
2525                         .filter     = filter_group_entries,
2526                         .nr_entries = nr_entries,
2527                         .priv       = evlist,
2528                 },
2529                 .min_pcnt = min_pcnt,
2530                 .env = env,
2531         };
2532
2533         ui_helpline__push("Press ESC to exit");
2534
2535         evlist__for_each(evlist, pos) {
2536                 const char *ev_name = perf_evsel__name(pos);
2537                 size_t line_len = strlen(ev_name) + 7;
2538
2539                 if (menu.b.width < line_len)
2540                         menu.b.width = line_len;
2541         }
2542
2543         return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2544 }
2545
2546 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2547                                   struct hist_browser_timer *hbt,
2548                                   float min_pcnt,
2549                                   struct perf_env *env)
2550 {
2551         int nr_entries = evlist->nr_entries;
2552
2553 single_entry:
2554         if (nr_entries == 1) {
2555                 struct perf_evsel *first = perf_evlist__first(evlist);
2556
2557                 return perf_evsel__hists_browse(first, nr_entries, help,
2558                                                 false, hbt, min_pcnt,
2559                                                 env);
2560         }
2561
2562         if (symbol_conf.event_group) {
2563                 struct perf_evsel *pos;
2564
2565                 nr_entries = 0;
2566                 evlist__for_each(evlist, pos) {
2567                         if (perf_evsel__is_group_leader(pos))
2568                                 nr_entries++;
2569                 }
2570
2571                 if (nr_entries == 1)
2572                         goto single_entry;
2573         }
2574
2575         return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2576                                                hbt, min_pcnt, env);
2577 }
This page took 0.190137 seconds and 4 git commands to generate.