Linux 4.1.18
[linux/fpc-iii.git] / tools / perf / ui / browsers / hists.c
blob658b0a89796d231e9350b941b7135e17cd4c13e5
1 #include <stdio.h>
2 #include "../libslang.h"
3 #include <stdlib.h>
4 #include <string.h>
5 #include <linux/rbtree.h>
7 #include "../../util/evsel.h"
8 #include "../../util/evlist.h"
9 #include "../../util/hist.h"
10 #include "../../util/pstack.h"
11 #include "../../util/sort.h"
12 #include "../../util/util.h"
13 #include "../../util/top.h"
14 #include "../../arch/common.h"
16 #include "../browser.h"
17 #include "../helpline.h"
18 #include "../util.h"
19 #include "../ui.h"
20 #include "map.h"
21 #include "annotate.h"
23 struct hist_browser {
24 struct ui_browser b;
25 struct hists *hists;
26 struct hist_entry *he_selection;
27 struct map_symbol *selection;
28 int print_seq;
29 bool show_dso;
30 bool show_headers;
31 float min_pcnt;
32 u64 nr_non_filtered_entries;
33 u64 nr_callchain_rows;
36 extern void hist_browser__init_hpp(void);
38 static int hists__browser_title(struct hists *hists,
39 struct hist_browser_timer *hbt,
40 char *bf, size_t size);
41 static void hist_browser__update_nr_entries(struct hist_browser *hb);
43 static struct rb_node *hists__filter_entries(struct rb_node *nd,
44 float min_pcnt);
46 static bool hist_browser__has_filter(struct hist_browser *hb)
48 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
51 static int hist_browser__get_folding(struct hist_browser *browser)
53 struct rb_node *nd;
54 struct hists *hists = browser->hists;
55 int unfolded_rows = 0;
57 for (nd = rb_first(&hists->entries);
58 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
59 nd = rb_next(nd)) {
60 struct hist_entry *he =
61 rb_entry(nd, struct hist_entry, rb_node);
63 if (he->ms.unfolded)
64 unfolded_rows += he->nr_rows;
66 return unfolded_rows;
69 static u32 hist_browser__nr_entries(struct hist_browser *hb)
71 u32 nr_entries;
73 if (hist_browser__has_filter(hb))
74 nr_entries = hb->nr_non_filtered_entries;
75 else
76 nr_entries = hb->hists->nr_entries;
78 hb->nr_callchain_rows = hist_browser__get_folding(hb);
79 return nr_entries + hb->nr_callchain_rows;
82 static void hist_browser__update_rows(struct hist_browser *hb)
84 struct ui_browser *browser = &hb->b;
85 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
87 browser->rows = browser->height - header_offset;
89 * Verify if we were at the last line and that line isn't
90 * visibe because we now show the header line(s).
92 index_row = browser->index - browser->top_idx;
93 if (index_row >= browser->rows)
94 browser->index -= index_row - browser->rows + 1;
97 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
99 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
101 /* 3 == +/- toggle symbol before actual hist_entry rendering */
102 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
104 * FIXME: Just keeping existing behaviour, but this really should be
105 * before updating browser->width, as it will invalidate the
106 * calculation above. Fix this and the fallout in another
107 * changeset.
109 ui_browser__refresh_dimensions(browser);
110 hist_browser__update_rows(hb);
113 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
115 u16 header_offset = browser->show_headers ? 1 : 0;
117 ui_browser__gotorc(&browser->b, row + header_offset, column);
120 static void hist_browser__reset(struct hist_browser *browser)
123 * The hists__remove_entry_filter() already folds non-filtered
124 * entries so we can assume it has 0 callchain rows.
126 browser->nr_callchain_rows = 0;
128 hist_browser__update_nr_entries(browser);
129 browser->b.nr_entries = hist_browser__nr_entries(browser);
130 hist_browser__refresh_dimensions(&browser->b);
131 ui_browser__reset_index(&browser->b);
134 static char tree__folded_sign(bool unfolded)
136 return unfolded ? '-' : '+';
139 static char map_symbol__folded(const struct map_symbol *ms)
141 return ms->has_children ? tree__folded_sign(ms->unfolded) : ' ';
144 static char hist_entry__folded(const struct hist_entry *he)
146 return map_symbol__folded(&he->ms);
149 static char callchain_list__folded(const struct callchain_list *cl)
151 return map_symbol__folded(&cl->ms);
154 static void map_symbol__set_folding(struct map_symbol *ms, bool unfold)
156 ms->unfolded = unfold ? ms->has_children : false;
159 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
161 int n = 0;
162 struct rb_node *nd;
164 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
165 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
166 struct callchain_list *chain;
167 char folded_sign = ' '; /* No children */
169 list_for_each_entry(chain, &child->val, list) {
170 ++n;
171 /* We need this because we may not have children */
172 folded_sign = callchain_list__folded(chain);
173 if (folded_sign == '+')
174 break;
177 if (folded_sign == '-') /* Have children and they're unfolded */
178 n += callchain_node__count_rows_rb_tree(child);
181 return n;
184 static int callchain_node__count_rows(struct callchain_node *node)
186 struct callchain_list *chain;
187 bool unfolded = false;
188 int n = 0;
190 list_for_each_entry(chain, &node->val, list) {
191 ++n;
192 unfolded = chain->ms.unfolded;
195 if (unfolded)
196 n += callchain_node__count_rows_rb_tree(node);
198 return n;
201 static int callchain__count_rows(struct rb_root *chain)
203 struct rb_node *nd;
204 int n = 0;
206 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
207 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
208 n += callchain_node__count_rows(node);
211 return n;
214 static bool map_symbol__toggle_fold(struct map_symbol *ms)
216 if (!ms)
217 return false;
219 if (!ms->has_children)
220 return false;
222 ms->unfolded = !ms->unfolded;
223 return true;
226 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
228 struct rb_node *nd = rb_first(&node->rb_root);
230 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
231 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
232 struct callchain_list *chain;
233 bool first = true;
235 list_for_each_entry(chain, &child->val, list) {
236 if (first) {
237 first = false;
238 chain->ms.has_children = chain->list.next != &child->val ||
239 !RB_EMPTY_ROOT(&child->rb_root);
240 } else
241 chain->ms.has_children = chain->list.next == &child->val &&
242 !RB_EMPTY_ROOT(&child->rb_root);
245 callchain_node__init_have_children_rb_tree(child);
249 static void callchain_node__init_have_children(struct callchain_node *node,
250 bool has_sibling)
252 struct callchain_list *chain;
254 chain = list_entry(node->val.next, struct callchain_list, list);
255 chain->ms.has_children = has_sibling;
257 if (!list_empty(&node->val)) {
258 chain = list_entry(node->val.prev, struct callchain_list, list);
259 chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root);
262 callchain_node__init_have_children_rb_tree(node);
265 static void callchain__init_have_children(struct rb_root *root)
267 struct rb_node *nd = rb_first(root);
268 bool has_sibling = nd && rb_next(nd);
270 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
271 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
272 callchain_node__init_have_children(node, has_sibling);
276 static void hist_entry__init_have_children(struct hist_entry *he)
278 if (!he->init_have_children) {
279 he->ms.has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
280 callchain__init_have_children(&he->sorted_chain);
281 he->init_have_children = true;
285 static bool hist_browser__toggle_fold(struct hist_browser *browser)
287 if (map_symbol__toggle_fold(browser->selection)) {
288 struct hist_entry *he = browser->he_selection;
290 hist_entry__init_have_children(he);
291 browser->b.nr_entries -= he->nr_rows;
292 browser->nr_callchain_rows -= he->nr_rows;
294 if (he->ms.unfolded)
295 he->nr_rows = callchain__count_rows(&he->sorted_chain);
296 else
297 he->nr_rows = 0;
299 browser->b.nr_entries += he->nr_rows;
300 browser->nr_callchain_rows += he->nr_rows;
302 return true;
305 /* If it doesn't have children, no toggling performed */
306 return false;
309 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
311 int n = 0;
312 struct rb_node *nd;
314 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
315 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
316 struct callchain_list *chain;
317 bool has_children = false;
319 list_for_each_entry(chain, &child->val, list) {
320 ++n;
321 map_symbol__set_folding(&chain->ms, unfold);
322 has_children = chain->ms.has_children;
325 if (has_children)
326 n += callchain_node__set_folding_rb_tree(child, unfold);
329 return n;
332 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
334 struct callchain_list *chain;
335 bool has_children = false;
336 int n = 0;
338 list_for_each_entry(chain, &node->val, list) {
339 ++n;
340 map_symbol__set_folding(&chain->ms, unfold);
341 has_children = chain->ms.has_children;
344 if (has_children)
345 n += callchain_node__set_folding_rb_tree(node, unfold);
347 return n;
350 static int callchain__set_folding(struct rb_root *chain, bool unfold)
352 struct rb_node *nd;
353 int n = 0;
355 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
356 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
357 n += callchain_node__set_folding(node, unfold);
360 return n;
363 static void hist_entry__set_folding(struct hist_entry *he, bool unfold)
365 hist_entry__init_have_children(he);
366 map_symbol__set_folding(&he->ms, unfold);
368 if (he->ms.has_children) {
369 int n = callchain__set_folding(&he->sorted_chain, unfold);
370 he->nr_rows = unfold ? n : 0;
371 } else
372 he->nr_rows = 0;
375 static void
376 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
378 struct rb_node *nd;
379 struct hists *hists = browser->hists;
381 for (nd = rb_first(&hists->entries);
382 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
383 nd = rb_next(nd)) {
384 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
385 hist_entry__set_folding(he, unfold);
386 browser->nr_callchain_rows += he->nr_rows;
390 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
392 browser->nr_callchain_rows = 0;
393 __hist_browser__set_folding(browser, unfold);
395 browser->b.nr_entries = hist_browser__nr_entries(browser);
396 /* Go to the start, we may be way after valid entries after a collapse */
397 ui_browser__reset_index(&browser->b);
400 static void ui_browser__warn_lost_events(struct ui_browser *browser)
402 ui_browser__warning(browser, 4,
403 "Events are being lost, check IO/CPU overload!\n\n"
404 "You may want to run 'perf' using a RT scheduler policy:\n\n"
405 " perf top -r 80\n\n"
406 "Or reduce the sampling frequency.");
409 static int hist_browser__run(struct hist_browser *browser,
410 struct hist_browser_timer *hbt)
412 int key;
413 char title[160];
414 int delay_secs = hbt ? hbt->refresh : 0;
416 browser->b.entries = &browser->hists->entries;
417 browser->b.nr_entries = hist_browser__nr_entries(browser);
419 hists__browser_title(browser->hists, hbt, title, sizeof(title));
421 if (ui_browser__show(&browser->b, title,
422 "Press '?' for help on key bindings") < 0)
423 return -1;
425 while (1) {
426 key = ui_browser__run(&browser->b, delay_secs);
428 switch (key) {
429 case K_TIMER: {
430 u64 nr_entries;
431 hbt->timer(hbt->arg);
433 if (hist_browser__has_filter(browser))
434 hist_browser__update_nr_entries(browser);
436 nr_entries = hist_browser__nr_entries(browser);
437 ui_browser__update_nr_entries(&browser->b, nr_entries);
439 if (browser->hists->stats.nr_lost_warned !=
440 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
441 browser->hists->stats.nr_lost_warned =
442 browser->hists->stats.nr_events[PERF_RECORD_LOST];
443 ui_browser__warn_lost_events(&browser->b);
446 hists__browser_title(browser->hists,
447 hbt, title, sizeof(title));
448 ui_browser__show_title(&browser->b, title);
449 continue;
451 case 'D': { /* Debug */
452 static int seq;
453 struct hist_entry *h = rb_entry(browser->b.top,
454 struct hist_entry, rb_node);
455 ui_helpline__pop();
456 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
457 seq++, browser->b.nr_entries,
458 browser->hists->nr_entries,
459 browser->b.rows,
460 browser->b.index,
461 browser->b.top_idx,
462 h->row_offset, h->nr_rows);
464 break;
465 case 'C':
466 /* Collapse the whole world. */
467 hist_browser__set_folding(browser, false);
468 break;
469 case 'E':
470 /* Expand the whole world. */
471 hist_browser__set_folding(browser, true);
472 break;
473 case 'H':
474 browser->show_headers = !browser->show_headers;
475 hist_browser__update_rows(browser);
476 break;
477 case K_ENTER:
478 if (hist_browser__toggle_fold(browser))
479 break;
480 /* fall thru */
481 default:
482 goto out;
485 out:
486 ui_browser__hide(&browser->b);
487 return key;
490 struct callchain_print_arg {
491 /* for hists browser */
492 off_t row_offset;
493 bool is_current_entry;
495 /* for file dump */
496 FILE *fp;
497 int printed;
500 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
501 struct callchain_list *chain,
502 const char *str, int offset,
503 unsigned short row,
504 struct callchain_print_arg *arg);
506 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
507 struct callchain_list *chain,
508 const char *str, int offset,
509 unsigned short row,
510 struct callchain_print_arg *arg)
512 int color, width;
513 char folded_sign = callchain_list__folded(chain);
514 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
516 color = HE_COLORSET_NORMAL;
517 width = browser->b.width - (offset + 2);
518 if (ui_browser__is_current_entry(&browser->b, row)) {
519 browser->selection = &chain->ms;
520 color = HE_COLORSET_SELECTED;
521 arg->is_current_entry = true;
524 ui_browser__set_color(&browser->b, color);
525 hist_browser__gotorc(browser, row, 0);
526 slsmg_write_nstring(" ", offset);
527 slsmg_printf("%c", folded_sign);
528 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
529 slsmg_write_nstring(str, width);
532 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
533 struct callchain_list *chain,
534 const char *str, int offset,
535 unsigned short row __maybe_unused,
536 struct callchain_print_arg *arg)
538 char folded_sign = callchain_list__folded(chain);
540 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
541 folded_sign, str);
544 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
545 unsigned short row);
547 static bool hist_browser__check_output_full(struct hist_browser *browser,
548 unsigned short row)
550 return browser->b.rows == row;
553 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
554 unsigned short row __maybe_unused)
556 return false;
559 #define LEVEL_OFFSET_STEP 3
561 static int hist_browser__show_callchain(struct hist_browser *browser,
562 struct rb_root *root, int level,
563 unsigned short row, u64 total,
564 print_callchain_entry_fn print,
565 struct callchain_print_arg *arg,
566 check_output_full_fn is_output_full)
568 struct rb_node *node;
569 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
570 u64 new_total;
571 bool need_percent;
573 node = rb_first(root);
574 need_percent = node && rb_next(node);
576 while (node) {
577 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
578 struct rb_node *next = rb_next(node);
579 u64 cumul = callchain_cumul_hits(child);
580 struct callchain_list *chain;
581 char folded_sign = ' ';
582 int first = true;
583 int extra_offset = 0;
585 list_for_each_entry(chain, &child->val, list) {
586 char bf[1024], *alloc_str;
587 const char *str;
588 bool was_first = first;
590 if (first)
591 first = false;
592 else if (need_percent)
593 extra_offset = LEVEL_OFFSET_STEP;
595 folded_sign = callchain_list__folded(chain);
596 if (arg->row_offset != 0) {
597 arg->row_offset--;
598 goto do_next;
601 alloc_str = NULL;
602 str = callchain_list__sym_name(chain, bf, sizeof(bf),
603 browser->show_dso);
605 if (was_first && need_percent) {
606 double percent = cumul * 100.0 / total;
608 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
609 str = "Not enough memory!";
610 else
611 str = alloc_str;
614 print(browser, chain, str, offset + extra_offset, row, arg);
616 free(alloc_str);
618 if (is_output_full(browser, ++row))
619 goto out;
620 do_next:
621 if (folded_sign == '+')
622 break;
625 if (folded_sign == '-') {
626 const int new_level = level + (extra_offset ? 2 : 1);
628 if (callchain_param.mode == CHAIN_GRAPH_REL)
629 new_total = child->children_hit;
630 else
631 new_total = total;
633 row += hist_browser__show_callchain(browser, &child->rb_root,
634 new_level, row, new_total,
635 print, arg, is_output_full);
637 if (is_output_full(browser, row))
638 break;
639 node = next;
641 out:
642 return row - first_row;
645 struct hpp_arg {
646 struct ui_browser *b;
647 char folded_sign;
648 bool current_entry;
651 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
653 struct hpp_arg *arg = hpp->ptr;
654 int ret, len;
655 va_list args;
656 double percent;
658 va_start(args, fmt);
659 len = va_arg(args, int);
660 percent = va_arg(args, double);
661 va_end(args);
663 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
665 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
666 slsmg_printf("%s", hpp->buf);
668 advance_hpp(hpp, ret);
669 return ret;
672 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
673 static u64 __hpp_get_##_field(struct hist_entry *he) \
675 return he->stat._field; \
678 static int \
679 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
680 struct perf_hpp *hpp, \
681 struct hist_entry *he) \
683 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
684 __hpp__slsmg_color_printf, true); \
687 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
688 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
690 return he->stat_acc->_field; \
693 static int \
694 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
695 struct perf_hpp *hpp, \
696 struct hist_entry *he) \
698 if (!symbol_conf.cumulate_callchain) { \
699 int len = fmt->user_len ?: fmt->len; \
700 int ret = scnprintf(hpp->buf, hpp->size, \
701 "%*s", len, "N/A"); \
702 slsmg_printf("%s", hpp->buf); \
704 return ret; \
706 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
707 " %*.2f%%", __hpp__slsmg_color_printf, true); \
710 __HPP_COLOR_PERCENT_FN(overhead, period)
711 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
712 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
713 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
714 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
715 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
717 #undef __HPP_COLOR_PERCENT_FN
718 #undef __HPP_COLOR_ACC_PERCENT_FN
720 void hist_browser__init_hpp(void)
722 perf_hpp__format[PERF_HPP__OVERHEAD].color =
723 hist_browser__hpp_color_overhead;
724 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
725 hist_browser__hpp_color_overhead_sys;
726 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
727 hist_browser__hpp_color_overhead_us;
728 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
729 hist_browser__hpp_color_overhead_guest_sys;
730 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
731 hist_browser__hpp_color_overhead_guest_us;
732 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
733 hist_browser__hpp_color_overhead_acc;
736 static int hist_browser__show_entry(struct hist_browser *browser,
737 struct hist_entry *entry,
738 unsigned short row)
740 char s[256];
741 int printed = 0;
742 int width = browser->b.width;
743 char folded_sign = ' ';
744 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
745 off_t row_offset = entry->row_offset;
746 bool first = true;
747 struct perf_hpp_fmt *fmt;
749 if (current_entry) {
750 browser->he_selection = entry;
751 browser->selection = &entry->ms;
754 if (symbol_conf.use_callchain) {
755 hist_entry__init_have_children(entry);
756 folded_sign = hist_entry__folded(entry);
759 if (row_offset == 0) {
760 struct hpp_arg arg = {
761 .b = &browser->b,
762 .folded_sign = folded_sign,
763 .current_entry = current_entry,
765 struct perf_hpp hpp = {
766 .buf = s,
767 .size = sizeof(s),
768 .ptr = &arg,
771 hist_browser__gotorc(browser, row, 0);
773 perf_hpp__for_each_format(fmt) {
774 if (perf_hpp__should_skip(fmt))
775 continue;
777 if (current_entry && browser->b.navkeypressed) {
778 ui_browser__set_color(&browser->b,
779 HE_COLORSET_SELECTED);
780 } else {
781 ui_browser__set_color(&browser->b,
782 HE_COLORSET_NORMAL);
785 if (first) {
786 if (symbol_conf.use_callchain) {
787 slsmg_printf("%c ", folded_sign);
788 width -= 2;
790 first = false;
791 } else {
792 slsmg_printf(" ");
793 width -= 2;
796 if (fmt->color) {
797 width -= fmt->color(fmt, &hpp, entry);
798 } else {
799 width -= fmt->entry(fmt, &hpp, entry);
800 slsmg_printf("%s", s);
804 /* The scroll bar isn't being used */
805 if (!browser->b.navkeypressed)
806 width += 1;
808 slsmg_write_nstring("", width);
810 ++row;
811 ++printed;
812 } else
813 --row_offset;
815 if (folded_sign == '-' && row != browser->b.rows) {
816 u64 total = hists__total_period(entry->hists);
817 struct callchain_print_arg arg = {
818 .row_offset = row_offset,
819 .is_current_entry = current_entry,
822 if (callchain_param.mode == CHAIN_GRAPH_REL) {
823 if (symbol_conf.cumulate_callchain)
824 total = entry->stat_acc->period;
825 else
826 total = entry->stat.period;
829 printed += hist_browser__show_callchain(browser,
830 &entry->sorted_chain, 1, row, total,
831 hist_browser__show_callchain_entry, &arg,
832 hist_browser__check_output_full);
834 if (arg.is_current_entry)
835 browser->he_selection = entry;
838 return printed;
841 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
843 advance_hpp(hpp, inc);
844 return hpp->size <= 0;
847 static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists)
849 struct perf_hpp dummy_hpp = {
850 .buf = buf,
851 .size = size,
853 struct perf_hpp_fmt *fmt;
854 size_t ret = 0;
856 if (symbol_conf.use_callchain) {
857 ret = scnprintf(buf, size, " ");
858 if (advance_hpp_check(&dummy_hpp, ret))
859 return ret;
862 perf_hpp__for_each_format(fmt) {
863 if (perf_hpp__should_skip(fmt))
864 continue;
866 ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
867 if (advance_hpp_check(&dummy_hpp, ret))
868 break;
870 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
871 if (advance_hpp_check(&dummy_hpp, ret))
872 break;
875 return ret;
878 static void hist_browser__show_headers(struct hist_browser *browser)
880 char headers[1024];
882 hists__scnprintf_headers(headers, sizeof(headers), browser->hists);
883 ui_browser__gotorc(&browser->b, 0, 0);
884 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
885 slsmg_write_nstring(headers, browser->b.width + 1);
888 static void ui_browser__hists_init_top(struct ui_browser *browser)
890 if (browser->top == NULL) {
891 struct hist_browser *hb;
893 hb = container_of(browser, struct hist_browser, b);
894 browser->top = rb_first(&hb->hists->entries);
898 static unsigned int hist_browser__refresh(struct ui_browser *browser)
900 unsigned row = 0;
901 u16 header_offset = 0;
902 struct rb_node *nd;
903 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
905 if (hb->show_headers) {
906 hist_browser__show_headers(hb);
907 header_offset = 1;
910 ui_browser__hists_init_top(browser);
912 for (nd = browser->top; nd; nd = rb_next(nd)) {
913 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
914 float percent;
916 if (h->filtered)
917 continue;
919 percent = hist_entry__get_percent_limit(h);
920 if (percent < hb->min_pcnt)
921 continue;
923 row += hist_browser__show_entry(hb, h, row);
924 if (row == browser->rows)
925 break;
928 return row + header_offset;
931 static struct rb_node *hists__filter_entries(struct rb_node *nd,
932 float min_pcnt)
934 while (nd != NULL) {
935 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
936 float percent = hist_entry__get_percent_limit(h);
938 if (!h->filtered && percent >= min_pcnt)
939 return nd;
941 nd = rb_next(nd);
944 return NULL;
947 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
948 float min_pcnt)
950 while (nd != NULL) {
951 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
952 float percent = hist_entry__get_percent_limit(h);
954 if (!h->filtered && percent >= min_pcnt)
955 return nd;
957 nd = rb_prev(nd);
960 return NULL;
963 static void ui_browser__hists_seek(struct ui_browser *browser,
964 off_t offset, int whence)
966 struct hist_entry *h;
967 struct rb_node *nd;
968 bool first = true;
969 struct hist_browser *hb;
971 hb = container_of(browser, struct hist_browser, b);
973 if (browser->nr_entries == 0)
974 return;
976 ui_browser__hists_init_top(browser);
978 switch (whence) {
979 case SEEK_SET:
980 nd = hists__filter_entries(rb_first(browser->entries),
981 hb->min_pcnt);
982 break;
983 case SEEK_CUR:
984 nd = browser->top;
985 goto do_offset;
986 case SEEK_END:
987 nd = hists__filter_prev_entries(rb_last(browser->entries),
988 hb->min_pcnt);
989 first = false;
990 break;
991 default:
992 return;
996 * Moves not relative to the first visible entry invalidates its
997 * row_offset:
999 h = rb_entry(browser->top, struct hist_entry, rb_node);
1000 h->row_offset = 0;
1003 * Here we have to check if nd is expanded (+), if it is we can't go
1004 * the next top level hist_entry, instead we must compute an offset of
1005 * what _not_ to show and not change the first visible entry.
1007 * This offset increments when we are going from top to bottom and
1008 * decreases when we're going from bottom to top.
1010 * As we don't have backpointers to the top level in the callchains
1011 * structure, we need to always print the whole hist_entry callchain,
1012 * skipping the first ones that are before the first visible entry
1013 * and stop when we printed enough lines to fill the screen.
1015 do_offset:
1016 if (offset > 0) {
1017 do {
1018 h = rb_entry(nd, struct hist_entry, rb_node);
1019 if (h->ms.unfolded) {
1020 u16 remaining = h->nr_rows - h->row_offset;
1021 if (offset > remaining) {
1022 offset -= remaining;
1023 h->row_offset = 0;
1024 } else {
1025 h->row_offset += offset;
1026 offset = 0;
1027 browser->top = nd;
1028 break;
1031 nd = hists__filter_entries(rb_next(nd), hb->min_pcnt);
1032 if (nd == NULL)
1033 break;
1034 --offset;
1035 browser->top = nd;
1036 } while (offset != 0);
1037 } else if (offset < 0) {
1038 while (1) {
1039 h = rb_entry(nd, struct hist_entry, rb_node);
1040 if (h->ms.unfolded) {
1041 if (first) {
1042 if (-offset > h->row_offset) {
1043 offset += h->row_offset;
1044 h->row_offset = 0;
1045 } else {
1046 h->row_offset += offset;
1047 offset = 0;
1048 browser->top = nd;
1049 break;
1051 } else {
1052 if (-offset > h->nr_rows) {
1053 offset += h->nr_rows;
1054 h->row_offset = 0;
1055 } else {
1056 h->row_offset = h->nr_rows + offset;
1057 offset = 0;
1058 browser->top = nd;
1059 break;
1064 nd = hists__filter_prev_entries(rb_prev(nd),
1065 hb->min_pcnt);
1066 if (nd == NULL)
1067 break;
1068 ++offset;
1069 browser->top = nd;
1070 if (offset == 0) {
1072 * Last unfiltered hist_entry, check if it is
1073 * unfolded, if it is then we should have
1074 * row_offset at its last entry.
1076 h = rb_entry(nd, struct hist_entry, rb_node);
1077 if (h->ms.unfolded)
1078 h->row_offset = h->nr_rows;
1079 break;
1081 first = false;
1083 } else {
1084 browser->top = nd;
1085 h = rb_entry(nd, struct hist_entry, rb_node);
1086 h->row_offset = 0;
1090 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1091 struct hist_entry *he, FILE *fp)
1093 u64 total = hists__total_period(he->hists);
1094 struct callchain_print_arg arg = {
1095 .fp = fp,
1098 if (symbol_conf.cumulate_callchain)
1099 total = he->stat_acc->period;
1101 hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
1102 hist_browser__fprintf_callchain_entry, &arg,
1103 hist_browser__check_dump_full);
1104 return arg.printed;
1107 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1108 struct hist_entry *he, FILE *fp)
1110 char s[8192];
1111 int printed = 0;
1112 char folded_sign = ' ';
1113 struct perf_hpp hpp = {
1114 .buf = s,
1115 .size = sizeof(s),
1117 struct perf_hpp_fmt *fmt;
1118 bool first = true;
1119 int ret;
1121 if (symbol_conf.use_callchain)
1122 folded_sign = hist_entry__folded(he);
1124 if (symbol_conf.use_callchain)
1125 printed += fprintf(fp, "%c ", folded_sign);
1127 perf_hpp__for_each_format(fmt) {
1128 if (perf_hpp__should_skip(fmt))
1129 continue;
1131 if (!first) {
1132 ret = scnprintf(hpp.buf, hpp.size, " ");
1133 advance_hpp(&hpp, ret);
1134 } else
1135 first = false;
1137 ret = fmt->entry(fmt, &hpp, he);
1138 advance_hpp(&hpp, ret);
1140 printed += fprintf(fp, "%s\n", rtrim(s));
1142 if (folded_sign == '-')
1143 printed += hist_browser__fprintf_callchain(browser, he, fp);
1145 return printed;
1148 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1150 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1151 browser->min_pcnt);
1152 int printed = 0;
1154 while (nd) {
1155 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1157 printed += hist_browser__fprintf_entry(browser, h, fp);
1158 nd = hists__filter_entries(rb_next(nd), browser->min_pcnt);
1161 return printed;
1164 static int hist_browser__dump(struct hist_browser *browser)
1166 char filename[64];
1167 FILE *fp;
1169 while (1) {
1170 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
1171 if (access(filename, F_OK))
1172 break;
1174 * XXX: Just an arbitrary lazy upper limit
1176 if (++browser->print_seq == 8192) {
1177 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
1178 return -1;
1182 fp = fopen(filename, "w");
1183 if (fp == NULL) {
1184 char bf[64];
1185 const char *err = strerror_r(errno, bf, sizeof(bf));
1186 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
1187 return -1;
1190 ++browser->print_seq;
1191 hist_browser__fprintf(browser, fp);
1192 fclose(fp);
1193 ui_helpline__fpush("%s written!", filename);
1195 return 0;
1198 static struct hist_browser *hist_browser__new(struct hists *hists)
1200 struct hist_browser *browser = zalloc(sizeof(*browser));
1202 if (browser) {
1203 browser->hists = hists;
1204 browser->b.refresh = hist_browser__refresh;
1205 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
1206 browser->b.seek = ui_browser__hists_seek;
1207 browser->b.use_navkeypressed = true;
1208 browser->show_headers = symbol_conf.show_hist_headers;
1211 return browser;
1214 static void hist_browser__delete(struct hist_browser *browser)
1216 free(browser);
1219 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
1221 return browser->he_selection;
1224 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
1226 return browser->he_selection->thread;
1229 /* Check whether the browser is for 'top' or 'report' */
1230 static inline bool is_report_browser(void *timer)
1232 return timer == NULL;
1235 static int hists__browser_title(struct hists *hists,
1236 struct hist_browser_timer *hbt,
1237 char *bf, size_t size)
1239 char unit;
1240 int printed;
1241 const struct dso *dso = hists->dso_filter;
1242 const struct thread *thread = hists->thread_filter;
1243 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1244 u64 nr_events = hists->stats.total_period;
1245 struct perf_evsel *evsel = hists_to_evsel(hists);
1246 const char *ev_name = perf_evsel__name(evsel);
1247 char buf[512];
1248 size_t buflen = sizeof(buf);
1250 if (symbol_conf.filter_relative) {
1251 nr_samples = hists->stats.nr_non_filtered_samples;
1252 nr_events = hists->stats.total_non_filtered_period;
1255 if (perf_evsel__is_group_event(evsel)) {
1256 struct perf_evsel *pos;
1258 perf_evsel__group_desc(evsel, buf, buflen);
1259 ev_name = buf;
1261 for_each_group_member(pos, evsel) {
1262 struct hists *pos_hists = evsel__hists(pos);
1264 if (symbol_conf.filter_relative) {
1265 nr_samples += pos_hists->stats.nr_non_filtered_samples;
1266 nr_events += pos_hists->stats.total_non_filtered_period;
1267 } else {
1268 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1269 nr_events += pos_hists->stats.total_period;
1274 nr_samples = convert_unit(nr_samples, &unit);
1275 printed = scnprintf(bf, size,
1276 "Samples: %lu%c of event '%s', Event count (approx.): %" PRIu64,
1277 nr_samples, unit, ev_name, nr_events);
1280 if (hists->uid_filter_str)
1281 printed += snprintf(bf + printed, size - printed,
1282 ", UID: %s", hists->uid_filter_str);
1283 if (thread)
1284 printed += scnprintf(bf + printed, size - printed,
1285 ", Thread: %s(%d)",
1286 (thread->comm_set ? thread__comm_str(thread) : ""),
1287 thread->tid);
1288 if (dso)
1289 printed += scnprintf(bf + printed, size - printed,
1290 ", DSO: %s", dso->short_name);
1291 if (!is_report_browser(hbt)) {
1292 struct perf_top *top = hbt->arg;
1294 if (top->zero)
1295 printed += scnprintf(bf + printed, size - printed, " [z]");
1298 return printed;
1301 static inline void free_popup_options(char **options, int n)
1303 int i;
1305 for (i = 0; i < n; ++i)
1306 zfree(&options[i]);
1310 * Only runtime switching of perf data file will make "input_name" point
1311 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
1312 * whether we need to call free() for current "input_name" during the switch.
1314 static bool is_input_name_malloced = false;
1316 static int switch_data_file(void)
1318 char *pwd, *options[32], *abs_path[32], *tmp;
1319 DIR *pwd_dir;
1320 int nr_options = 0, choice = -1, ret = -1;
1321 struct dirent *dent;
1323 pwd = getenv("PWD");
1324 if (!pwd)
1325 return ret;
1327 pwd_dir = opendir(pwd);
1328 if (!pwd_dir)
1329 return ret;
1331 memset(options, 0, sizeof(options));
1332 memset(options, 0, sizeof(abs_path));
1334 while ((dent = readdir(pwd_dir))) {
1335 char path[PATH_MAX];
1336 u64 magic;
1337 char *name = dent->d_name;
1338 FILE *file;
1340 if (!(dent->d_type == DT_REG))
1341 continue;
1343 snprintf(path, sizeof(path), "%s/%s", pwd, name);
1345 file = fopen(path, "r");
1346 if (!file)
1347 continue;
1349 if (fread(&magic, 1, 8, file) < 8)
1350 goto close_file_and_continue;
1352 if (is_perf_magic(magic)) {
1353 options[nr_options] = strdup(name);
1354 if (!options[nr_options])
1355 goto close_file_and_continue;
1357 abs_path[nr_options] = strdup(path);
1358 if (!abs_path[nr_options]) {
1359 zfree(&options[nr_options]);
1360 ui__warning("Can't search all data files due to memory shortage.\n");
1361 fclose(file);
1362 break;
1365 nr_options++;
1368 close_file_and_continue:
1369 fclose(file);
1370 if (nr_options >= 32) {
1371 ui__warning("Too many perf data files in PWD!\n"
1372 "Only the first 32 files will be listed.\n");
1373 break;
1376 closedir(pwd_dir);
1378 if (nr_options) {
1379 choice = ui__popup_menu(nr_options, options);
1380 if (choice < nr_options && choice >= 0) {
1381 tmp = strdup(abs_path[choice]);
1382 if (tmp) {
1383 if (is_input_name_malloced)
1384 free((void *)input_name);
1385 input_name = tmp;
1386 is_input_name_malloced = true;
1387 ret = 0;
1388 } else
1389 ui__warning("Data switch failed due to memory shortage!\n");
1393 free_popup_options(options, nr_options);
1394 free_popup_options(abs_path, nr_options);
1395 return ret;
1398 static void hist_browser__update_nr_entries(struct hist_browser *hb)
1400 u64 nr_entries = 0;
1401 struct rb_node *nd = rb_first(&hb->hists->entries);
1403 if (hb->min_pcnt == 0) {
1404 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
1405 return;
1408 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
1409 nr_entries++;
1410 nd = rb_next(nd);
1413 hb->nr_non_filtered_entries = nr_entries;
1416 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
1417 const char *helpline,
1418 bool left_exits,
1419 struct hist_browser_timer *hbt,
1420 float min_pcnt,
1421 struct perf_session_env *env)
1423 struct hists *hists = evsel__hists(evsel);
1424 struct hist_browser *browser = hist_browser__new(hists);
1425 struct branch_info *bi;
1426 struct pstack *fstack;
1427 char *options[16];
1428 int nr_options = 0;
1429 int key = -1;
1430 char buf[64];
1431 char script_opt[64];
1432 int delay_secs = hbt ? hbt->refresh : 0;
1433 struct perf_hpp_fmt *fmt;
1435 #define HIST_BROWSER_HELP_COMMON \
1436 "h/?/F1 Show this window\n" \
1437 "UP/DOWN/PGUP\n" \
1438 "PGDN/SPACE Navigate\n" \
1439 "q/ESC/CTRL+C Exit browser\n\n" \
1440 "For multiple event sessions:\n\n" \
1441 "TAB/UNTAB Switch events\n\n" \
1442 "For symbolic views (--sort has sym):\n\n" \
1443 "-> Zoom into DSO/Threads & Annotate current symbol\n" \
1444 "<- Zoom out\n" \
1445 "a Annotate current symbol\n" \
1446 "C Collapse all callchains\n" \
1447 "d Zoom into current DSO\n" \
1448 "E Expand all callchains\n" \
1449 "F Toggle percentage of filtered entries\n" \
1450 "H Display column headers\n" \
1452 /* help messages are sorted by lexical order of the hotkey */
1453 const char report_help[] = HIST_BROWSER_HELP_COMMON
1454 "i Show header information\n"
1455 "P Print histograms to perf.hist.N\n"
1456 "r Run available scripts\n"
1457 "s Switch to another data file in PWD\n"
1458 "t Zoom into current Thread\n"
1459 "V Verbose (DSO names in callchains, etc)\n"
1460 "/ Filter symbol by name";
1461 const char top_help[] = HIST_BROWSER_HELP_COMMON
1462 "P Print histograms to perf.hist.N\n"
1463 "t Zoom into current Thread\n"
1464 "V Verbose (DSO names in callchains, etc)\n"
1465 "z Toggle zeroing of samples\n"
1466 "/ Filter symbol by name";
1468 if (browser == NULL)
1469 return -1;
1471 if (min_pcnt) {
1472 browser->min_pcnt = min_pcnt;
1473 hist_browser__update_nr_entries(browser);
1476 fstack = pstack__new(2);
1477 if (fstack == NULL)
1478 goto out;
1480 ui_helpline__push(helpline);
1482 memset(options, 0, sizeof(options));
1484 perf_hpp__for_each_format(fmt)
1485 perf_hpp__reset_width(fmt, hists);
1487 if (symbol_conf.col_width_list_str)
1488 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
1490 while (1) {
1491 struct thread *thread = NULL;
1492 const struct dso *dso = NULL;
1493 int choice = 0,
1494 annotate = -2, zoom_dso = -2, zoom_thread = -2,
1495 annotate_f = -2, annotate_t = -2, browse_map = -2;
1496 int scripts_comm = -2, scripts_symbol = -2,
1497 scripts_all = -2, switch_data = -2;
1499 nr_options = 0;
1501 key = hist_browser__run(browser, hbt);
1503 if (browser->he_selection != NULL) {
1504 thread = hist_browser__selected_thread(browser);
1505 dso = browser->selection->map ? browser->selection->map->dso : NULL;
1507 switch (key) {
1508 case K_TAB:
1509 case K_UNTAB:
1510 if (nr_events == 1)
1511 continue;
1513 * Exit the browser, let hists__browser_tree
1514 * go to the next or previous
1516 goto out_free_stack;
1517 case 'a':
1518 if (!sort__has_sym) {
1519 ui_browser__warning(&browser->b, delay_secs * 2,
1520 "Annotation is only available for symbolic views, "
1521 "include \"sym*\" in --sort to use it.");
1522 continue;
1525 if (browser->selection == NULL ||
1526 browser->selection->sym == NULL ||
1527 browser->selection->map->dso->annotate_warned)
1528 continue;
1529 goto do_annotate;
1530 case 'P':
1531 hist_browser__dump(browser);
1532 continue;
1533 case 'd':
1534 goto zoom_dso;
1535 case 'V':
1536 browser->show_dso = !browser->show_dso;
1537 continue;
1538 case 't':
1539 goto zoom_thread;
1540 case '/':
1541 if (ui_browser__input_window("Symbol to show",
1542 "Please enter the name of symbol you want to see",
1543 buf, "ENTER: OK, ESC: Cancel",
1544 delay_secs * 2) == K_ENTER) {
1545 hists->symbol_filter_str = *buf ? buf : NULL;
1546 hists__filter_by_symbol(hists);
1547 hist_browser__reset(browser);
1549 continue;
1550 case 'r':
1551 if (is_report_browser(hbt))
1552 goto do_scripts;
1553 continue;
1554 case 's':
1555 if (is_report_browser(hbt))
1556 goto do_data_switch;
1557 continue;
1558 case 'i':
1559 /* env->arch is NULL for live-mode (i.e. perf top) */
1560 if (env->arch)
1561 tui__header_window(env);
1562 continue;
1563 case 'F':
1564 symbol_conf.filter_relative ^= 1;
1565 continue;
1566 case 'z':
1567 if (!is_report_browser(hbt)) {
1568 struct perf_top *top = hbt->arg;
1570 top->zero = !top->zero;
1572 continue;
1573 case K_F1:
1574 case 'h':
1575 case '?':
1576 ui_browser__help_window(&browser->b,
1577 is_report_browser(hbt) ? report_help : top_help);
1578 continue;
1579 case K_ENTER:
1580 case K_RIGHT:
1581 /* menu */
1582 break;
1583 case K_LEFT: {
1584 const void *top;
1586 if (pstack__empty(fstack)) {
1588 * Go back to the perf_evsel_menu__run or other user
1590 if (left_exits)
1591 goto out_free_stack;
1592 continue;
1594 top = pstack__pop(fstack);
1595 if (top == &browser->hists->dso_filter)
1596 goto zoom_out_dso;
1597 if (top == &browser->hists->thread_filter)
1598 goto zoom_out_thread;
1599 continue;
1601 case K_ESC:
1602 if (!left_exits &&
1603 !ui_browser__dialog_yesno(&browser->b,
1604 "Do you really want to exit?"))
1605 continue;
1606 /* Fall thru */
1607 case 'q':
1608 case CTRL('c'):
1609 goto out_free_stack;
1610 default:
1611 continue;
1614 if (!sort__has_sym)
1615 goto add_exit_option;
1617 if (browser->selection == NULL)
1618 goto skip_annotation;
1620 if (sort__mode == SORT_MODE__BRANCH) {
1621 bi = browser->he_selection->branch_info;
1623 if (bi == NULL)
1624 goto skip_annotation;
1626 if (bi->from.sym != NULL &&
1627 !bi->from.map->dso->annotate_warned &&
1628 asprintf(&options[nr_options], "Annotate %s", bi->from.sym->name) > 0) {
1629 annotate_f = nr_options++;
1632 if (bi->to.sym != NULL &&
1633 !bi->to.map->dso->annotate_warned &&
1634 (bi->to.sym != bi->from.sym ||
1635 bi->to.map->dso != bi->from.map->dso) &&
1636 asprintf(&options[nr_options], "Annotate %s", bi->to.sym->name) > 0) {
1637 annotate_t = nr_options++;
1639 } else {
1640 if (browser->selection->sym != NULL &&
1641 !browser->selection->map->dso->annotate_warned) {
1642 struct annotation *notes;
1644 notes = symbol__annotation(browser->selection->sym);
1646 if (notes->src &&
1647 asprintf(&options[nr_options], "Annotate %s",
1648 browser->selection->sym->name) > 0) {
1649 annotate = nr_options++;
1653 skip_annotation:
1654 if (thread != NULL &&
1655 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1656 (browser->hists->thread_filter ? "out of" : "into"),
1657 (thread->comm_set ? thread__comm_str(thread) : ""),
1658 thread->tid) > 0)
1659 zoom_thread = nr_options++;
1661 if (dso != NULL &&
1662 asprintf(&options[nr_options], "Zoom %s %s DSO",
1663 (browser->hists->dso_filter ? "out of" : "into"),
1664 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1665 zoom_dso = nr_options++;
1667 if (browser->selection != NULL &&
1668 browser->selection->map != NULL &&
1669 asprintf(&options[nr_options], "Browse map details") > 0)
1670 browse_map = nr_options++;
1672 /* perf script support */
1673 if (browser->he_selection) {
1674 struct symbol *sym;
1676 if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]",
1677 thread__comm_str(browser->he_selection->thread)) > 0)
1678 scripts_comm = nr_options++;
1680 sym = browser->he_selection->ms.sym;
1681 if (sym && sym->namelen &&
1682 asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]",
1683 sym->name) > 0)
1684 scripts_symbol = nr_options++;
1687 if (asprintf(&options[nr_options], "Run scripts for all samples") > 0)
1688 scripts_all = nr_options++;
1690 if (is_report_browser(hbt) && asprintf(&options[nr_options],
1691 "Switch to another data file in PWD") > 0)
1692 switch_data = nr_options++;
1693 add_exit_option:
1694 options[nr_options++] = (char *)"Exit";
1695 retry_popup_menu:
1696 choice = ui__popup_menu(nr_options, options);
1698 if (choice == nr_options - 1)
1699 break;
1701 if (choice == -1) {
1702 free_popup_options(options, nr_options - 1);
1703 continue;
1706 if (choice == annotate || choice == annotate_t || choice == annotate_f) {
1707 struct hist_entry *he;
1708 struct annotation *notes;
1709 struct map_symbol ms;
1710 int err;
1711 do_annotate:
1712 if (!objdump_path && perf_session_env__lookup_objdump(env))
1713 continue;
1715 he = hist_browser__selected_entry(browser);
1716 if (he == NULL)
1717 continue;
1719 if (choice == annotate_f) {
1720 ms.map = he->branch_info->from.map;
1721 ms.sym = he->branch_info->from.sym;
1722 } else if (choice == annotate_t) {
1723 ms.map = he->branch_info->to.map;
1724 ms.sym = he->branch_info->to.sym;
1725 } else {
1726 ms = *browser->selection;
1729 notes = symbol__annotation(ms.sym);
1730 if (!notes->src)
1731 continue;
1733 err = map_symbol__tui_annotate(&ms, evsel, hbt);
1735 * offer option to annotate the other branch source or target
1736 * (if they exists) when returning from annotate
1738 if ((err == 'q' || err == CTRL('c'))
1739 && annotate_t != -2 && annotate_f != -2)
1740 goto retry_popup_menu;
1742 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
1743 if (err)
1744 ui_browser__handle_resize(&browser->b);
1746 } else if (choice == browse_map)
1747 map__browse(browser->selection->map);
1748 else if (choice == zoom_dso) {
1749 zoom_dso:
1750 if (browser->hists->dso_filter) {
1751 pstack__remove(fstack, &browser->hists->dso_filter);
1752 zoom_out_dso:
1753 ui_helpline__pop();
1754 browser->hists->dso_filter = NULL;
1755 perf_hpp__set_elide(HISTC_DSO, false);
1756 } else {
1757 if (dso == NULL)
1758 continue;
1759 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1760 dso->kernel ? "the Kernel" : dso->short_name);
1761 browser->hists->dso_filter = dso;
1762 perf_hpp__set_elide(HISTC_DSO, true);
1763 pstack__push(fstack, &browser->hists->dso_filter);
1765 hists__filter_by_dso(hists);
1766 hist_browser__reset(browser);
1767 } else if (choice == zoom_thread) {
1768 zoom_thread:
1769 if (browser->hists->thread_filter) {
1770 pstack__remove(fstack, &browser->hists->thread_filter);
1771 zoom_out_thread:
1772 ui_helpline__pop();
1773 thread__zput(browser->hists->thread_filter);
1774 perf_hpp__set_elide(HISTC_THREAD, false);
1775 } else {
1776 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1777 thread->comm_set ? thread__comm_str(thread) : "",
1778 thread->tid);
1779 browser->hists->thread_filter = thread__get(thread);
1780 perf_hpp__set_elide(HISTC_THREAD, false);
1781 pstack__push(fstack, &browser->hists->thread_filter);
1783 hists__filter_by_thread(hists);
1784 hist_browser__reset(browser);
1786 /* perf scripts support */
1787 else if (choice == scripts_all || choice == scripts_comm ||
1788 choice == scripts_symbol) {
1789 do_scripts:
1790 memset(script_opt, 0, 64);
1792 if (choice == scripts_comm)
1793 sprintf(script_opt, " -c %s ", thread__comm_str(browser->he_selection->thread));
1795 if (choice == scripts_symbol)
1796 sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name);
1798 script_browse(script_opt);
1800 /* Switch to another data file */
1801 else if (choice == switch_data) {
1802 do_data_switch:
1803 if (!switch_data_file()) {
1804 key = K_SWITCH_INPUT_DATA;
1805 break;
1806 } else
1807 ui__warning("Won't switch the data files due to\n"
1808 "no valid data file get selected!\n");
1811 out_free_stack:
1812 pstack__delete(fstack);
1813 out:
1814 hist_browser__delete(browser);
1815 free_popup_options(options, nr_options - 1);
1816 return key;
1819 struct perf_evsel_menu {
1820 struct ui_browser b;
1821 struct perf_evsel *selection;
1822 bool lost_events, lost_events_warned;
1823 float min_pcnt;
1824 struct perf_session_env *env;
1827 static void perf_evsel_menu__write(struct ui_browser *browser,
1828 void *entry, int row)
1830 struct perf_evsel_menu *menu = container_of(browser,
1831 struct perf_evsel_menu, b);
1832 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1833 struct hists *hists = evsel__hists(evsel);
1834 bool current_entry = ui_browser__is_current_entry(browser, row);
1835 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1836 const char *ev_name = perf_evsel__name(evsel);
1837 char bf[256], unit;
1838 const char *warn = " ";
1839 size_t printed;
1841 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1842 HE_COLORSET_NORMAL);
1844 if (perf_evsel__is_group_event(evsel)) {
1845 struct perf_evsel *pos;
1847 ev_name = perf_evsel__group_name(evsel);
1849 for_each_group_member(pos, evsel) {
1850 struct hists *pos_hists = evsel__hists(pos);
1851 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
1855 nr_events = convert_unit(nr_events, &unit);
1856 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1857 unit, unit == ' ' ? "" : " ", ev_name);
1858 slsmg_printf("%s", bf);
1860 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
1861 if (nr_events != 0) {
1862 menu->lost_events = true;
1863 if (!current_entry)
1864 ui_browser__set_color(browser, HE_COLORSET_TOP);
1865 nr_events = convert_unit(nr_events, &unit);
1866 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
1867 nr_events, unit, unit == ' ' ? "" : " ");
1868 warn = bf;
1871 slsmg_write_nstring(warn, browser->width - printed);
1873 if (current_entry)
1874 menu->selection = evsel;
1877 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1878 int nr_events, const char *help,
1879 struct hist_browser_timer *hbt)
1881 struct perf_evlist *evlist = menu->b.priv;
1882 struct perf_evsel *pos;
1883 const char *title = "Available samples";
1884 int delay_secs = hbt ? hbt->refresh : 0;
1885 int key;
1887 if (ui_browser__show(&menu->b, title,
1888 "ESC: exit, ENTER|->: Browse histograms") < 0)
1889 return -1;
1891 while (1) {
1892 key = ui_browser__run(&menu->b, delay_secs);
1894 switch (key) {
1895 case K_TIMER:
1896 hbt->timer(hbt->arg);
1898 if (!menu->lost_events_warned && menu->lost_events) {
1899 ui_browser__warn_lost_events(&menu->b);
1900 menu->lost_events_warned = true;
1902 continue;
1903 case K_RIGHT:
1904 case K_ENTER:
1905 if (!menu->selection)
1906 continue;
1907 pos = menu->selection;
1908 browse_hists:
1909 perf_evlist__set_selected(evlist, pos);
1911 * Give the calling tool a chance to populate the non
1912 * default evsel resorted hists tree.
1914 if (hbt)
1915 hbt->timer(hbt->arg);
1916 key = perf_evsel__hists_browse(pos, nr_events, help,
1917 true, hbt,
1918 menu->min_pcnt,
1919 menu->env);
1920 ui_browser__show_title(&menu->b, title);
1921 switch (key) {
1922 case K_TAB:
1923 if (pos->node.next == &evlist->entries)
1924 pos = perf_evlist__first(evlist);
1925 else
1926 pos = perf_evsel__next(pos);
1927 goto browse_hists;
1928 case K_UNTAB:
1929 if (pos->node.prev == &evlist->entries)
1930 pos = perf_evlist__last(evlist);
1931 else
1932 pos = perf_evsel__prev(pos);
1933 goto browse_hists;
1934 case K_ESC:
1935 if (!ui_browser__dialog_yesno(&menu->b,
1936 "Do you really want to exit?"))
1937 continue;
1938 /* Fall thru */
1939 case K_SWITCH_INPUT_DATA:
1940 case 'q':
1941 case CTRL('c'):
1942 goto out;
1943 default:
1944 continue;
1946 case K_LEFT:
1947 continue;
1948 case K_ESC:
1949 if (!ui_browser__dialog_yesno(&menu->b,
1950 "Do you really want to exit?"))
1951 continue;
1952 /* Fall thru */
1953 case 'q':
1954 case CTRL('c'):
1955 goto out;
1956 default:
1957 continue;
1961 out:
1962 ui_browser__hide(&menu->b);
1963 return key;
1966 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
1967 void *entry)
1969 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1971 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1972 return true;
1974 return false;
1977 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1978 int nr_entries, const char *help,
1979 struct hist_browser_timer *hbt,
1980 float min_pcnt,
1981 struct perf_session_env *env)
1983 struct perf_evsel *pos;
1984 struct perf_evsel_menu menu = {
1985 .b = {
1986 .entries = &evlist->entries,
1987 .refresh = ui_browser__list_head_refresh,
1988 .seek = ui_browser__list_head_seek,
1989 .write = perf_evsel_menu__write,
1990 .filter = filter_group_entries,
1991 .nr_entries = nr_entries,
1992 .priv = evlist,
1994 .min_pcnt = min_pcnt,
1995 .env = env,
1998 ui_helpline__push("Press ESC to exit");
2000 evlist__for_each(evlist, pos) {
2001 const char *ev_name = perf_evsel__name(pos);
2002 size_t line_len = strlen(ev_name) + 7;
2004 if (menu.b.width < line_len)
2005 menu.b.width = line_len;
2008 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
2011 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
2012 struct hist_browser_timer *hbt,
2013 float min_pcnt,
2014 struct perf_session_env *env)
2016 int nr_entries = evlist->nr_entries;
2018 single_entry:
2019 if (nr_entries == 1) {
2020 struct perf_evsel *first = perf_evlist__first(evlist);
2022 return perf_evsel__hists_browse(first, nr_entries, help,
2023 false, hbt, min_pcnt,
2024 env);
2027 if (symbol_conf.event_group) {
2028 struct perf_evsel *pos;
2030 nr_entries = 0;
2031 evlist__for_each(evlist, pos) {
2032 if (perf_evsel__is_group_leader(pos))
2033 nr_entries++;
2036 if (nr_entries == 1)
2037 goto single_entry;
2040 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
2041 hbt, min_pcnt, env);