Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cris-mirror.git] / tools / perf / ui / browsers / hists.c
blob13d414384739d9673089958aa2daa56de9a5b255
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <linux/rbtree.h>
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"
15 #include "../browsers/hists.h"
16 #include "../helpline.h"
17 #include "../util.h"
18 #include "../ui.h"
19 #include "map.h"
20 #include "annotate.h"
22 extern void hist_browser__init_hpp(void);
24 static int perf_evsel_browser_title(struct hist_browser *browser,
25 char *bf, size_t size);
26 static void hist_browser__update_nr_entries(struct hist_browser *hb);
28 static struct rb_node *hists__filter_entries(struct rb_node *nd,
29 float min_pcnt);
31 static bool hist_browser__has_filter(struct hist_browser *hb)
33 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter;
36 static int hist_browser__get_folding(struct hist_browser *browser)
38 struct rb_node *nd;
39 struct hists *hists = browser->hists;
40 int unfolded_rows = 0;
42 for (nd = rb_first(&hists->entries);
43 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
44 nd = rb_hierarchy_next(nd)) {
45 struct hist_entry *he =
46 rb_entry(nd, struct hist_entry, rb_node);
48 if (he->leaf && he->unfolded)
49 unfolded_rows += he->nr_rows;
51 return unfolded_rows;
54 static u32 hist_browser__nr_entries(struct hist_browser *hb)
56 u32 nr_entries;
58 if (symbol_conf.report_hierarchy)
59 nr_entries = hb->nr_hierarchy_entries;
60 else if (hist_browser__has_filter(hb))
61 nr_entries = hb->nr_non_filtered_entries;
62 else
63 nr_entries = hb->hists->nr_entries;
65 hb->nr_callchain_rows = hist_browser__get_folding(hb);
66 return nr_entries + hb->nr_callchain_rows;
69 static void hist_browser__update_rows(struct hist_browser *hb)
71 struct ui_browser *browser = &hb->b;
72 u16 header_offset = hb->show_headers ? 1 : 0, index_row;
74 browser->rows = browser->height - header_offset;
76 * Verify if we were at the last line and that line isn't
77 * visibe because we now show the header line(s).
79 index_row = browser->index - browser->top_idx;
80 if (index_row >= browser->rows)
81 browser->index -= index_row - browser->rows + 1;
84 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
86 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
88 /* 3 == +/- toggle symbol before actual hist_entry rendering */
89 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
91 * FIXME: Just keeping existing behaviour, but this really should be
92 * before updating browser->width, as it will invalidate the
93 * calculation above. Fix this and the fallout in another
94 * changeset.
96 ui_browser__refresh_dimensions(browser);
97 hist_browser__update_rows(hb);
100 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
102 u16 header_offset = browser->show_headers ? 1 : 0;
104 ui_browser__gotorc(&browser->b, row + header_offset, column);
107 static void hist_browser__reset(struct hist_browser *browser)
110 * The hists__remove_entry_filter() already folds non-filtered
111 * entries so we can assume it has 0 callchain rows.
113 browser->nr_callchain_rows = 0;
115 hist_browser__update_nr_entries(browser);
116 browser->b.nr_entries = hist_browser__nr_entries(browser);
117 hist_browser__refresh_dimensions(&browser->b);
118 ui_browser__reset_index(&browser->b);
121 static char tree__folded_sign(bool unfolded)
123 return unfolded ? '-' : '+';
126 static char hist_entry__folded(const struct hist_entry *he)
128 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
131 static char callchain_list__folded(const struct callchain_list *cl)
133 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
136 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
138 cl->unfolded = unfold ? cl->has_children : false;
141 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
143 int n = 0;
144 struct rb_node *nd;
146 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
147 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
148 struct callchain_list *chain;
149 char folded_sign = ' '; /* No children */
151 list_for_each_entry(chain, &child->val, list) {
152 ++n;
153 /* We need this because we may not have children */
154 folded_sign = callchain_list__folded(chain);
155 if (folded_sign == '+')
156 break;
159 if (folded_sign == '-') /* Have children and they're unfolded */
160 n += callchain_node__count_rows_rb_tree(child);
163 return n;
166 static int callchain_node__count_flat_rows(struct callchain_node *node)
168 struct callchain_list *chain;
169 char folded_sign = 0;
170 int n = 0;
172 list_for_each_entry(chain, &node->parent_val, list) {
173 if (!folded_sign) {
174 /* only check first chain list entry */
175 folded_sign = callchain_list__folded(chain);
176 if (folded_sign == '+')
177 return 1;
179 n++;
182 list_for_each_entry(chain, &node->val, list) {
183 if (!folded_sign) {
184 /* node->parent_val list might be empty */
185 folded_sign = callchain_list__folded(chain);
186 if (folded_sign == '+')
187 return 1;
189 n++;
192 return n;
195 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
197 return 1;
200 static int callchain_node__count_rows(struct callchain_node *node)
202 struct callchain_list *chain;
203 bool unfolded = false;
204 int n = 0;
206 if (callchain_param.mode == CHAIN_FLAT)
207 return callchain_node__count_flat_rows(node);
208 else if (callchain_param.mode == CHAIN_FOLDED)
209 return callchain_node__count_folded_rows(node);
211 list_for_each_entry(chain, &node->val, list) {
212 ++n;
213 unfolded = chain->unfolded;
216 if (unfolded)
217 n += callchain_node__count_rows_rb_tree(node);
219 return n;
222 static int callchain__count_rows(struct rb_root *chain)
224 struct rb_node *nd;
225 int n = 0;
227 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
228 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
229 n += callchain_node__count_rows(node);
232 return n;
235 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
236 bool include_children)
238 int count = 0;
239 struct rb_node *node;
240 struct hist_entry *child;
242 if (he->leaf)
243 return callchain__count_rows(&he->sorted_chain);
245 if (he->has_no_entry)
246 return 1;
248 node = rb_first(&he->hroot_out);
249 while (node) {
250 float percent;
252 child = rb_entry(node, struct hist_entry, rb_node);
253 percent = hist_entry__get_percent_limit(child);
255 if (!child->filtered && percent >= hb->min_pcnt) {
256 count++;
258 if (include_children && child->unfolded)
259 count += hierarchy_count_rows(hb, child, true);
262 node = rb_next(node);
264 return count;
267 static bool hist_entry__toggle_fold(struct hist_entry *he)
269 if (!he)
270 return false;
272 if (!he->has_children)
273 return false;
275 he->unfolded = !he->unfolded;
276 return true;
279 static bool callchain_list__toggle_fold(struct callchain_list *cl)
281 if (!cl)
282 return false;
284 if (!cl->has_children)
285 return false;
287 cl->unfolded = !cl->unfolded;
288 return true;
291 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
293 struct rb_node *nd = rb_first(&node->rb_root);
295 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
296 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
297 struct callchain_list *chain;
298 bool first = true;
300 list_for_each_entry(chain, &child->val, list) {
301 if (first) {
302 first = false;
303 chain->has_children = chain->list.next != &child->val ||
304 !RB_EMPTY_ROOT(&child->rb_root);
305 } else
306 chain->has_children = chain->list.next == &child->val &&
307 !RB_EMPTY_ROOT(&child->rb_root);
310 callchain_node__init_have_children_rb_tree(child);
314 static void callchain_node__init_have_children(struct callchain_node *node,
315 bool has_sibling)
317 struct callchain_list *chain;
319 chain = list_entry(node->val.next, struct callchain_list, list);
320 chain->has_children = has_sibling;
322 if (!list_empty(&node->val)) {
323 chain = list_entry(node->val.prev, struct callchain_list, list);
324 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
327 callchain_node__init_have_children_rb_tree(node);
330 static void callchain__init_have_children(struct rb_root *root)
332 struct rb_node *nd = rb_first(root);
333 bool has_sibling = nd && rb_next(nd);
335 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
336 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
337 callchain_node__init_have_children(node, has_sibling);
338 if (callchain_param.mode == CHAIN_FLAT ||
339 callchain_param.mode == CHAIN_FOLDED)
340 callchain_node__make_parent_list(node);
344 static void hist_entry__init_have_children(struct hist_entry *he)
346 if (he->init_have_children)
347 return;
349 if (he->leaf) {
350 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
351 callchain__init_have_children(&he->sorted_chain);
352 } else {
353 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
356 he->init_have_children = true;
359 static bool hist_browser__toggle_fold(struct hist_browser *browser)
361 struct hist_entry *he = browser->he_selection;
362 struct map_symbol *ms = browser->selection;
363 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
364 bool has_children;
366 if (!he || !ms)
367 return false;
369 if (ms == &he->ms)
370 has_children = hist_entry__toggle_fold(he);
371 else
372 has_children = callchain_list__toggle_fold(cl);
374 if (has_children) {
375 int child_rows = 0;
377 hist_entry__init_have_children(he);
378 browser->b.nr_entries -= he->nr_rows;
380 if (he->leaf)
381 browser->nr_callchain_rows -= he->nr_rows;
382 else
383 browser->nr_hierarchy_entries -= he->nr_rows;
385 if (symbol_conf.report_hierarchy)
386 child_rows = hierarchy_count_rows(browser, he, true);
388 if (he->unfolded) {
389 if (he->leaf)
390 he->nr_rows = callchain__count_rows(&he->sorted_chain);
391 else
392 he->nr_rows = hierarchy_count_rows(browser, he, false);
394 /* account grand children */
395 if (symbol_conf.report_hierarchy)
396 browser->b.nr_entries += child_rows - he->nr_rows;
398 if (!he->leaf && he->nr_rows == 0) {
399 he->has_no_entry = true;
400 he->nr_rows = 1;
402 } else {
403 if (symbol_conf.report_hierarchy)
404 browser->b.nr_entries -= child_rows - he->nr_rows;
406 if (he->has_no_entry)
407 he->has_no_entry = false;
409 he->nr_rows = 0;
412 browser->b.nr_entries += he->nr_rows;
414 if (he->leaf)
415 browser->nr_callchain_rows += he->nr_rows;
416 else
417 browser->nr_hierarchy_entries += he->nr_rows;
419 return true;
422 /* If it doesn't have children, no toggling performed */
423 return false;
426 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
428 int n = 0;
429 struct rb_node *nd;
431 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
432 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
433 struct callchain_list *chain;
434 bool has_children = false;
436 list_for_each_entry(chain, &child->val, list) {
437 ++n;
438 callchain_list__set_folding(chain, unfold);
439 has_children = chain->has_children;
442 if (has_children)
443 n += callchain_node__set_folding_rb_tree(child, unfold);
446 return n;
449 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
451 struct callchain_list *chain;
452 bool has_children = false;
453 int n = 0;
455 list_for_each_entry(chain, &node->val, list) {
456 ++n;
457 callchain_list__set_folding(chain, unfold);
458 has_children = chain->has_children;
461 if (has_children)
462 n += callchain_node__set_folding_rb_tree(node, unfold);
464 return n;
467 static int callchain__set_folding(struct rb_root *chain, bool unfold)
469 struct rb_node *nd;
470 int n = 0;
472 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
473 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
474 n += callchain_node__set_folding(node, unfold);
477 return n;
480 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
481 bool unfold __maybe_unused)
483 float percent;
484 struct rb_node *nd;
485 struct hist_entry *child;
486 int n = 0;
488 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
489 child = rb_entry(nd, struct hist_entry, rb_node);
490 percent = hist_entry__get_percent_limit(child);
491 if (!child->filtered && percent >= hb->min_pcnt)
492 n++;
495 return n;
498 static void hist_entry__set_folding(struct hist_entry *he,
499 struct hist_browser *hb, bool unfold)
501 hist_entry__init_have_children(he);
502 he->unfolded = unfold ? he->has_children : false;
504 if (he->has_children) {
505 int n;
507 if (he->leaf)
508 n = callchain__set_folding(&he->sorted_chain, unfold);
509 else
510 n = hierarchy_set_folding(hb, he, unfold);
512 he->nr_rows = unfold ? n : 0;
513 } else
514 he->nr_rows = 0;
517 static void
518 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
520 struct rb_node *nd;
521 struct hist_entry *he;
522 double percent;
524 nd = rb_first(&browser->hists->entries);
525 while (nd) {
526 he = rb_entry(nd, struct hist_entry, rb_node);
528 /* set folding state even if it's currently folded */
529 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
531 hist_entry__set_folding(he, browser, unfold);
533 percent = hist_entry__get_percent_limit(he);
534 if (he->filtered || percent < browser->min_pcnt)
535 continue;
537 if (!he->depth || unfold)
538 browser->nr_hierarchy_entries++;
539 if (he->leaf)
540 browser->nr_callchain_rows += he->nr_rows;
541 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
542 browser->nr_hierarchy_entries++;
543 he->has_no_entry = true;
544 he->nr_rows = 1;
545 } else
546 he->has_no_entry = false;
550 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
552 browser->nr_hierarchy_entries = 0;
553 browser->nr_callchain_rows = 0;
554 __hist_browser__set_folding(browser, unfold);
556 browser->b.nr_entries = hist_browser__nr_entries(browser);
557 /* Go to the start, we may be way after valid entries after a collapse */
558 ui_browser__reset_index(&browser->b);
561 static void ui_browser__warn_lost_events(struct ui_browser *browser)
563 ui_browser__warning(browser, 4,
564 "Events are being lost, check IO/CPU overload!\n\n"
565 "You may want to run 'perf' using a RT scheduler policy:\n\n"
566 " perf top -r 80\n\n"
567 "Or reduce the sampling frequency.");
570 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
572 return browser->title ? browser->title(browser, bf, size) : 0;
575 int hist_browser__run(struct hist_browser *browser, const char *help)
577 int key;
578 char title[160];
579 struct hist_browser_timer *hbt = browser->hbt;
580 int delay_secs = hbt ? hbt->refresh : 0;
582 browser->b.entries = &browser->hists->entries;
583 browser->b.nr_entries = hist_browser__nr_entries(browser);
585 hist_browser__title(browser, title, sizeof(title));
587 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
588 return -1;
590 while (1) {
591 key = ui_browser__run(&browser->b, delay_secs);
593 switch (key) {
594 case K_TIMER: {
595 u64 nr_entries;
596 hbt->timer(hbt->arg);
598 if (hist_browser__has_filter(browser))
599 hist_browser__update_nr_entries(browser);
601 nr_entries = hist_browser__nr_entries(browser);
602 ui_browser__update_nr_entries(&browser->b, nr_entries);
604 if (browser->hists->stats.nr_lost_warned !=
605 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
606 browser->hists->stats.nr_lost_warned =
607 browser->hists->stats.nr_events[PERF_RECORD_LOST];
608 ui_browser__warn_lost_events(&browser->b);
611 hist_browser__title(browser, title, sizeof(title));
612 ui_browser__show_title(&browser->b, title);
613 continue;
615 case 'D': { /* Debug */
616 static int seq;
617 struct hist_entry *h = rb_entry(browser->b.top,
618 struct hist_entry, rb_node);
619 ui_helpline__pop();
620 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
621 seq++, browser->b.nr_entries,
622 browser->hists->nr_entries,
623 browser->b.rows,
624 browser->b.index,
625 browser->b.top_idx,
626 h->row_offset, h->nr_rows);
628 break;
629 case 'C':
630 /* Collapse the whole world. */
631 hist_browser__set_folding(browser, false);
632 break;
633 case 'E':
634 /* Expand the whole world. */
635 hist_browser__set_folding(browser, true);
636 break;
637 case 'H':
638 browser->show_headers = !browser->show_headers;
639 hist_browser__update_rows(browser);
640 break;
641 case K_ENTER:
642 if (hist_browser__toggle_fold(browser))
643 break;
644 /* fall thru */
645 default:
646 goto out;
649 out:
650 ui_browser__hide(&browser->b);
651 return key;
654 struct callchain_print_arg {
655 /* for hists browser */
656 off_t row_offset;
657 bool is_current_entry;
659 /* for file dump */
660 FILE *fp;
661 int printed;
664 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
665 struct callchain_list *chain,
666 const char *str, int offset,
667 unsigned short row,
668 struct callchain_print_arg *arg);
670 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
671 struct callchain_list *chain,
672 const char *str, int offset,
673 unsigned short row,
674 struct callchain_print_arg *arg)
676 int color, width;
677 char folded_sign = callchain_list__folded(chain);
678 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
680 color = HE_COLORSET_NORMAL;
681 width = browser->b.width - (offset + 2);
682 if (ui_browser__is_current_entry(&browser->b, row)) {
683 browser->selection = &chain->ms;
684 color = HE_COLORSET_SELECTED;
685 arg->is_current_entry = true;
688 ui_browser__set_color(&browser->b, color);
689 hist_browser__gotorc(browser, row, 0);
690 ui_browser__write_nstring(&browser->b, " ", offset);
691 ui_browser__printf(&browser->b, "%c", folded_sign);
692 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
693 ui_browser__write_nstring(&browser->b, str, width);
696 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
697 struct callchain_list *chain,
698 const char *str, int offset,
699 unsigned short row __maybe_unused,
700 struct callchain_print_arg *arg)
702 char folded_sign = callchain_list__folded(chain);
704 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
705 folded_sign, str);
708 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
709 unsigned short row);
711 static bool hist_browser__check_output_full(struct hist_browser *browser,
712 unsigned short row)
714 return browser->b.rows == row;
717 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
718 unsigned short row __maybe_unused)
720 return false;
723 #define LEVEL_OFFSET_STEP 3
725 static int hist_browser__show_callchain_list(struct hist_browser *browser,
726 struct callchain_node *node,
727 struct callchain_list *chain,
728 unsigned short row, u64 total,
729 bool need_percent, int offset,
730 print_callchain_entry_fn print,
731 struct callchain_print_arg *arg)
733 char bf[1024], *alloc_str;
734 const char *str;
736 if (arg->row_offset != 0) {
737 arg->row_offset--;
738 return 0;
741 alloc_str = NULL;
742 str = callchain_list__sym_name(chain, bf, sizeof(bf),
743 browser->show_dso);
745 if (need_percent) {
746 char buf[64];
748 callchain_node__scnprintf_value(node, buf, sizeof(buf),
749 total);
751 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
752 str = "Not enough memory!";
753 else
754 str = alloc_str;
757 print(browser, chain, str, offset, row, arg);
759 free(alloc_str);
760 return 1;
763 static bool check_percent_display(struct rb_node *node, u64 parent_total)
765 struct callchain_node *child;
767 if (node == NULL)
768 return false;
770 if (rb_next(node))
771 return true;
773 child = rb_entry(node, struct callchain_node, rb_node);
774 return callchain_cumul_hits(child) != parent_total;
777 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
778 struct rb_root *root,
779 unsigned short row, u64 total,
780 u64 parent_total,
781 print_callchain_entry_fn print,
782 struct callchain_print_arg *arg,
783 check_output_full_fn is_output_full)
785 struct rb_node *node;
786 int first_row = row, offset = LEVEL_OFFSET_STEP;
787 bool need_percent;
789 node = rb_first(root);
790 need_percent = check_percent_display(node, parent_total);
792 while (node) {
793 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
794 struct rb_node *next = rb_next(node);
795 struct callchain_list *chain;
796 char folded_sign = ' ';
797 int first = true;
798 int extra_offset = 0;
800 list_for_each_entry(chain, &child->parent_val, list) {
801 bool was_first = first;
803 if (first)
804 first = false;
805 else if (need_percent)
806 extra_offset = LEVEL_OFFSET_STEP;
808 folded_sign = callchain_list__folded(chain);
810 row += hist_browser__show_callchain_list(browser, child,
811 chain, row, total,
812 was_first && need_percent,
813 offset + extra_offset,
814 print, arg);
816 if (is_output_full(browser, row))
817 goto out;
819 if (folded_sign == '+')
820 goto next;
823 list_for_each_entry(chain, &child->val, list) {
824 bool was_first = first;
826 if (first)
827 first = false;
828 else if (need_percent)
829 extra_offset = LEVEL_OFFSET_STEP;
831 folded_sign = callchain_list__folded(chain);
833 row += hist_browser__show_callchain_list(browser, child,
834 chain, row, total,
835 was_first && need_percent,
836 offset + extra_offset,
837 print, arg);
839 if (is_output_full(browser, row))
840 goto out;
842 if (folded_sign == '+')
843 break;
846 next:
847 if (is_output_full(browser, row))
848 break;
849 node = next;
851 out:
852 return row - first_row;
855 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
856 struct callchain_list *chain,
857 char *value_str, char *old_str)
859 char bf[1024];
860 const char *str;
861 char *new;
863 str = callchain_list__sym_name(chain, bf, sizeof(bf),
864 browser->show_dso);
865 if (old_str) {
866 if (asprintf(&new, "%s%s%s", old_str,
867 symbol_conf.field_sep ?: ";", str) < 0)
868 new = NULL;
869 } else {
870 if (value_str) {
871 if (asprintf(&new, "%s %s", value_str, str) < 0)
872 new = NULL;
873 } else {
874 if (asprintf(&new, "%s", str) < 0)
875 new = NULL;
878 return new;
881 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
882 struct rb_root *root,
883 unsigned short row, u64 total,
884 u64 parent_total,
885 print_callchain_entry_fn print,
886 struct callchain_print_arg *arg,
887 check_output_full_fn is_output_full)
889 struct rb_node *node;
890 int first_row = row, offset = LEVEL_OFFSET_STEP;
891 bool need_percent;
893 node = rb_first(root);
894 need_percent = check_percent_display(node, parent_total);
896 while (node) {
897 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
898 struct rb_node *next = rb_next(node);
899 struct callchain_list *chain, *first_chain = NULL;
900 int first = true;
901 char *value_str = NULL, *value_str_alloc = NULL;
902 char *chain_str = NULL, *chain_str_alloc = NULL;
904 if (arg->row_offset != 0) {
905 arg->row_offset--;
906 goto next;
909 if (need_percent) {
910 char buf[64];
912 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
913 if (asprintf(&value_str, "%s", buf) < 0) {
914 value_str = (char *)"<...>";
915 goto do_print;
917 value_str_alloc = value_str;
920 list_for_each_entry(chain, &child->parent_val, list) {
921 chain_str = hist_browser__folded_callchain_str(browser,
922 chain, value_str, chain_str);
923 if (first) {
924 first = false;
925 first_chain = chain;
928 if (chain_str == NULL) {
929 chain_str = (char *)"Not enough memory!";
930 goto do_print;
933 chain_str_alloc = chain_str;
936 list_for_each_entry(chain, &child->val, list) {
937 chain_str = hist_browser__folded_callchain_str(browser,
938 chain, value_str, chain_str);
939 if (first) {
940 first = false;
941 first_chain = chain;
944 if (chain_str == NULL) {
945 chain_str = (char *)"Not enough memory!";
946 goto do_print;
949 chain_str_alloc = chain_str;
952 do_print:
953 print(browser, first_chain, chain_str, offset, row++, arg);
954 free(value_str_alloc);
955 free(chain_str_alloc);
957 next:
958 if (is_output_full(browser, row))
959 break;
960 node = next;
963 return row - first_row;
966 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
967 struct rb_root *root, int level,
968 unsigned short row, u64 total,
969 u64 parent_total,
970 print_callchain_entry_fn print,
971 struct callchain_print_arg *arg,
972 check_output_full_fn is_output_full)
974 struct rb_node *node;
975 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
976 bool need_percent;
977 u64 percent_total = total;
979 if (callchain_param.mode == CHAIN_GRAPH_REL)
980 percent_total = parent_total;
982 node = rb_first(root);
983 need_percent = check_percent_display(node, parent_total);
985 while (node) {
986 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
987 struct rb_node *next = rb_next(node);
988 struct callchain_list *chain;
989 char folded_sign = ' ';
990 int first = true;
991 int extra_offset = 0;
993 list_for_each_entry(chain, &child->val, list) {
994 bool was_first = first;
996 if (first)
997 first = false;
998 else if (need_percent)
999 extra_offset = LEVEL_OFFSET_STEP;
1001 folded_sign = callchain_list__folded(chain);
1003 row += hist_browser__show_callchain_list(browser, child,
1004 chain, row, percent_total,
1005 was_first && need_percent,
1006 offset + extra_offset,
1007 print, arg);
1009 if (is_output_full(browser, row))
1010 goto out;
1012 if (folded_sign == '+')
1013 break;
1016 if (folded_sign == '-') {
1017 const int new_level = level + (extra_offset ? 2 : 1);
1019 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1020 new_level, row, total,
1021 child->children_hit,
1022 print, arg, is_output_full);
1024 if (is_output_full(browser, row))
1025 break;
1026 node = next;
1028 out:
1029 return row - first_row;
1032 static int hist_browser__show_callchain(struct hist_browser *browser,
1033 struct hist_entry *entry, int level,
1034 unsigned short row,
1035 print_callchain_entry_fn print,
1036 struct callchain_print_arg *arg,
1037 check_output_full_fn is_output_full)
1039 u64 total = hists__total_period(entry->hists);
1040 u64 parent_total;
1041 int printed;
1043 if (symbol_conf.cumulate_callchain)
1044 parent_total = entry->stat_acc->period;
1045 else
1046 parent_total = entry->stat.period;
1048 if (callchain_param.mode == CHAIN_FLAT) {
1049 printed = hist_browser__show_callchain_flat(browser,
1050 &entry->sorted_chain, row,
1051 total, parent_total, print, arg,
1052 is_output_full);
1053 } else if (callchain_param.mode == CHAIN_FOLDED) {
1054 printed = hist_browser__show_callchain_folded(browser,
1055 &entry->sorted_chain, row,
1056 total, parent_total, print, arg,
1057 is_output_full);
1058 } else {
1059 printed = hist_browser__show_callchain_graph(browser,
1060 &entry->sorted_chain, level, row,
1061 total, parent_total, print, arg,
1062 is_output_full);
1065 if (arg->is_current_entry)
1066 browser->he_selection = entry;
1068 return printed;
1071 struct hpp_arg {
1072 struct ui_browser *b;
1073 char folded_sign;
1074 bool current_entry;
1077 static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1079 struct hpp_arg *arg = hpp->ptr;
1080 int ret, len;
1081 va_list args;
1082 double percent;
1084 va_start(args, fmt);
1085 len = va_arg(args, int);
1086 percent = va_arg(args, double);
1087 va_end(args);
1089 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1091 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1092 ui_browser__printf(arg->b, "%s", hpp->buf);
1094 advance_hpp(hpp, ret);
1095 return ret;
1098 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1099 static u64 __hpp_get_##_field(struct hist_entry *he) \
1101 return he->stat._field; \
1104 static int \
1105 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1106 struct perf_hpp *hpp, \
1107 struct hist_entry *he) \
1109 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1110 __hpp__slsmg_color_printf, true); \
1113 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1114 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1116 return he->stat_acc->_field; \
1119 static int \
1120 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1121 struct perf_hpp *hpp, \
1122 struct hist_entry *he) \
1124 if (!symbol_conf.cumulate_callchain) { \
1125 struct hpp_arg *arg = hpp->ptr; \
1126 int len = fmt->user_len ?: fmt->len; \
1127 int ret = scnprintf(hpp->buf, hpp->size, \
1128 "%*s", len, "N/A"); \
1129 ui_browser__printf(arg->b, "%s", hpp->buf); \
1131 return ret; \
1133 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1134 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1137 __HPP_COLOR_PERCENT_FN(overhead, period)
1138 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1139 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1140 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1141 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1142 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1144 #undef __HPP_COLOR_PERCENT_FN
1145 #undef __HPP_COLOR_ACC_PERCENT_FN
1147 void hist_browser__init_hpp(void)
1149 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1150 hist_browser__hpp_color_overhead;
1151 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1152 hist_browser__hpp_color_overhead_sys;
1153 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1154 hist_browser__hpp_color_overhead_us;
1155 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1156 hist_browser__hpp_color_overhead_guest_sys;
1157 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1158 hist_browser__hpp_color_overhead_guest_us;
1159 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1160 hist_browser__hpp_color_overhead_acc;
1163 static int hist_browser__show_entry(struct hist_browser *browser,
1164 struct hist_entry *entry,
1165 unsigned short row)
1167 int printed = 0;
1168 int width = browser->b.width;
1169 char folded_sign = ' ';
1170 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1171 off_t row_offset = entry->row_offset;
1172 bool first = true;
1173 struct perf_hpp_fmt *fmt;
1175 if (current_entry) {
1176 browser->he_selection = entry;
1177 browser->selection = &entry->ms;
1180 if (symbol_conf.use_callchain) {
1181 hist_entry__init_have_children(entry);
1182 folded_sign = hist_entry__folded(entry);
1185 if (row_offset == 0) {
1186 struct hpp_arg arg = {
1187 .b = &browser->b,
1188 .folded_sign = folded_sign,
1189 .current_entry = current_entry,
1191 int column = 0;
1193 hist_browser__gotorc(browser, row, 0);
1195 hists__for_each_format(browser->hists, fmt) {
1196 char s[2048];
1197 struct perf_hpp hpp = {
1198 .buf = s,
1199 .size = sizeof(s),
1200 .ptr = &arg,
1203 if (perf_hpp__should_skip(fmt, entry->hists) ||
1204 column++ < browser->b.horiz_scroll)
1205 continue;
1207 if (current_entry && browser->b.navkeypressed) {
1208 ui_browser__set_color(&browser->b,
1209 HE_COLORSET_SELECTED);
1210 } else {
1211 ui_browser__set_color(&browser->b,
1212 HE_COLORSET_NORMAL);
1215 if (first) {
1216 if (symbol_conf.use_callchain) {
1217 ui_browser__printf(&browser->b, "%c ", folded_sign);
1218 width -= 2;
1220 first = false;
1221 } else {
1222 ui_browser__printf(&browser->b, " ");
1223 width -= 2;
1226 if (fmt->color) {
1227 int ret = fmt->color(fmt, &hpp, entry);
1228 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1230 * fmt->color() already used ui_browser to
1231 * print the non alignment bits, skip it (+ret):
1233 ui_browser__printf(&browser->b, "%s", s + ret);
1234 } else {
1235 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1236 ui_browser__printf(&browser->b, "%s", s);
1238 width -= hpp.buf - s;
1241 /* The scroll bar isn't being used */
1242 if (!browser->b.navkeypressed)
1243 width += 1;
1245 ui_browser__write_nstring(&browser->b, "", width);
1247 ++row;
1248 ++printed;
1249 } else
1250 --row_offset;
1252 if (folded_sign == '-' && row != browser->b.rows) {
1253 struct callchain_print_arg arg = {
1254 .row_offset = row_offset,
1255 .is_current_entry = current_entry,
1258 printed += hist_browser__show_callchain(browser, entry, 1, row,
1259 hist_browser__show_callchain_entry, &arg,
1260 hist_browser__check_output_full);
1263 return printed;
1266 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1267 struct hist_entry *entry,
1268 unsigned short row,
1269 int level)
1271 int printed = 0;
1272 int width = browser->b.width;
1273 char folded_sign = ' ';
1274 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1275 off_t row_offset = entry->row_offset;
1276 bool first = true;
1277 struct perf_hpp_fmt *fmt;
1278 struct perf_hpp_list_node *fmt_node;
1279 struct hpp_arg arg = {
1280 .b = &browser->b,
1281 .current_entry = current_entry,
1283 int column = 0;
1284 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1286 if (current_entry) {
1287 browser->he_selection = entry;
1288 browser->selection = &entry->ms;
1291 hist_entry__init_have_children(entry);
1292 folded_sign = hist_entry__folded(entry);
1293 arg.folded_sign = folded_sign;
1295 if (entry->leaf && row_offset) {
1296 row_offset--;
1297 goto show_callchain;
1300 hist_browser__gotorc(browser, row, 0);
1302 if (current_entry && browser->b.navkeypressed)
1303 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1304 else
1305 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1307 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1308 width -= level * HIERARCHY_INDENT;
1310 /* the first hpp_list_node is for overhead columns */
1311 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1312 struct perf_hpp_list_node, list);
1313 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1314 char s[2048];
1315 struct perf_hpp hpp = {
1316 .buf = s,
1317 .size = sizeof(s),
1318 .ptr = &arg,
1321 if (perf_hpp__should_skip(fmt, entry->hists) ||
1322 column++ < browser->b.horiz_scroll)
1323 continue;
1325 if (current_entry && browser->b.navkeypressed) {
1326 ui_browser__set_color(&browser->b,
1327 HE_COLORSET_SELECTED);
1328 } else {
1329 ui_browser__set_color(&browser->b,
1330 HE_COLORSET_NORMAL);
1333 if (first) {
1334 ui_browser__printf(&browser->b, "%c", folded_sign);
1335 width--;
1336 first = false;
1337 } else {
1338 ui_browser__printf(&browser->b, " ");
1339 width -= 2;
1342 if (fmt->color) {
1343 int ret = fmt->color(fmt, &hpp, entry);
1344 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1346 * fmt->color() already used ui_browser to
1347 * print the non alignment bits, skip it (+ret):
1349 ui_browser__printf(&browser->b, "%s", s + ret);
1350 } else {
1351 int ret = fmt->entry(fmt, &hpp, entry);
1352 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1353 ui_browser__printf(&browser->b, "%s", s);
1355 width -= hpp.buf - s;
1358 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1359 width -= hierarchy_indent;
1361 if (column >= browser->b.horiz_scroll) {
1362 char s[2048];
1363 struct perf_hpp hpp = {
1364 .buf = s,
1365 .size = sizeof(s),
1366 .ptr = &arg,
1369 if (current_entry && browser->b.navkeypressed) {
1370 ui_browser__set_color(&browser->b,
1371 HE_COLORSET_SELECTED);
1372 } else {
1373 ui_browser__set_color(&browser->b,
1374 HE_COLORSET_NORMAL);
1377 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1378 ui_browser__write_nstring(&browser->b, "", 2);
1379 width -= 2;
1382 * No need to call hist_entry__snprintf_alignment()
1383 * since this fmt is always the last column in the
1384 * hierarchy mode.
1386 if (fmt->color) {
1387 width -= fmt->color(fmt, &hpp, entry);
1388 } else {
1389 int i = 0;
1391 width -= fmt->entry(fmt, &hpp, entry);
1392 ui_browser__printf(&browser->b, "%s", ltrim(s));
1394 while (isspace(s[i++]))
1395 width++;
1400 /* The scroll bar isn't being used */
1401 if (!browser->b.navkeypressed)
1402 width += 1;
1404 ui_browser__write_nstring(&browser->b, "", width);
1406 ++row;
1407 ++printed;
1409 show_callchain:
1410 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1411 struct callchain_print_arg carg = {
1412 .row_offset = row_offset,
1415 printed += hist_browser__show_callchain(browser, entry,
1416 level + 1, row,
1417 hist_browser__show_callchain_entry, &carg,
1418 hist_browser__check_output_full);
1421 return printed;
1424 static int hist_browser__show_no_entry(struct hist_browser *browser,
1425 unsigned short row, int level)
1427 int width = browser->b.width;
1428 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1429 bool first = true;
1430 int column = 0;
1431 int ret;
1432 struct perf_hpp_fmt *fmt;
1433 struct perf_hpp_list_node *fmt_node;
1434 int indent = browser->hists->nr_hpp_node - 2;
1436 if (current_entry) {
1437 browser->he_selection = NULL;
1438 browser->selection = NULL;
1441 hist_browser__gotorc(browser, row, 0);
1443 if (current_entry && browser->b.navkeypressed)
1444 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1445 else
1446 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1448 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1449 width -= level * HIERARCHY_INDENT;
1451 /* the first hpp_list_node is for overhead columns */
1452 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1453 struct perf_hpp_list_node, list);
1454 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1455 if (perf_hpp__should_skip(fmt, browser->hists) ||
1456 column++ < browser->b.horiz_scroll)
1457 continue;
1459 ret = fmt->width(fmt, NULL, browser->hists);
1461 if (first) {
1462 /* for folded sign */
1463 first = false;
1464 ret++;
1465 } else {
1466 /* space between columns */
1467 ret += 2;
1470 ui_browser__write_nstring(&browser->b, "", ret);
1471 width -= ret;
1474 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1475 width -= indent * HIERARCHY_INDENT;
1477 if (column >= browser->b.horiz_scroll) {
1478 char buf[32];
1480 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1481 ui_browser__printf(&browser->b, " %s", buf);
1482 width -= ret + 2;
1485 /* The scroll bar isn't being used */
1486 if (!browser->b.navkeypressed)
1487 width += 1;
1489 ui_browser__write_nstring(&browser->b, "", width);
1490 return 1;
1493 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1495 advance_hpp(hpp, inc);
1496 return hpp->size <= 0;
1499 static int hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf, size_t size)
1501 struct hists *hists = browser->hists;
1502 struct perf_hpp dummy_hpp = {
1503 .buf = buf,
1504 .size = size,
1506 struct perf_hpp_fmt *fmt;
1507 size_t ret = 0;
1508 int column = 0;
1510 if (symbol_conf.use_callchain) {
1511 ret = scnprintf(buf, size, " ");
1512 if (advance_hpp_check(&dummy_hpp, ret))
1513 return ret;
1516 hists__for_each_format(browser->hists, fmt) {
1517 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1518 continue;
1520 ret = fmt->header(fmt, &dummy_hpp, hists);
1521 if (advance_hpp_check(&dummy_hpp, ret))
1522 break;
1524 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1525 if (advance_hpp_check(&dummy_hpp, ret))
1526 break;
1529 return ret;
1532 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1534 struct hists *hists = browser->hists;
1535 struct perf_hpp dummy_hpp = {
1536 .buf = buf,
1537 .size = size,
1539 struct perf_hpp_fmt *fmt;
1540 struct perf_hpp_list_node *fmt_node;
1541 size_t ret = 0;
1542 int column = 0;
1543 int indent = hists->nr_hpp_node - 2;
1544 bool first_node, first_col;
1546 ret = scnprintf(buf, size, " ");
1547 if (advance_hpp_check(&dummy_hpp, ret))
1548 return ret;
1550 /* the first hpp_list_node is for overhead columns */
1551 fmt_node = list_first_entry(&hists->hpp_formats,
1552 struct perf_hpp_list_node, list);
1553 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1554 if (column++ < browser->b.horiz_scroll)
1555 continue;
1557 ret = fmt->header(fmt, &dummy_hpp, hists);
1558 if (advance_hpp_check(&dummy_hpp, ret))
1559 break;
1561 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1562 if (advance_hpp_check(&dummy_hpp, ret))
1563 break;
1566 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1567 indent * HIERARCHY_INDENT, "");
1568 if (advance_hpp_check(&dummy_hpp, ret))
1569 return ret;
1571 first_node = true;
1572 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1573 if (!first_node) {
1574 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1575 if (advance_hpp_check(&dummy_hpp, ret))
1576 break;
1578 first_node = false;
1580 first_col = true;
1581 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1582 char *start;
1584 if (perf_hpp__should_skip(fmt, hists))
1585 continue;
1587 if (!first_col) {
1588 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1589 if (advance_hpp_check(&dummy_hpp, ret))
1590 break;
1592 first_col = false;
1594 ret = fmt->header(fmt, &dummy_hpp, hists);
1595 dummy_hpp.buf[ret] = '\0';
1597 start = trim(dummy_hpp.buf);
1598 ret = strlen(start);
1600 if (start != dummy_hpp.buf)
1601 memmove(dummy_hpp.buf, start, ret + 1);
1603 if (advance_hpp_check(&dummy_hpp, ret))
1604 break;
1608 return ret;
1611 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1613 char headers[1024];
1615 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1616 sizeof(headers));
1618 ui_browser__gotorc(&browser->b, 0, 0);
1619 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1620 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1623 static void hists_browser__headers(struct hist_browser *browser)
1625 char headers[1024];
1627 hists_browser__scnprintf_headers(browser, headers,
1628 sizeof(headers));
1630 ui_browser__gotorc(&browser->b, 0, 0);
1631 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1632 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1635 static void hist_browser__show_headers(struct hist_browser *browser)
1637 if (symbol_conf.report_hierarchy)
1638 hists_browser__hierarchy_headers(browser);
1639 else
1640 hists_browser__headers(browser);
1643 static void ui_browser__hists_init_top(struct ui_browser *browser)
1645 if (browser->top == NULL) {
1646 struct hist_browser *hb;
1648 hb = container_of(browser, struct hist_browser, b);
1649 browser->top = rb_first(&hb->hists->entries);
1653 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1655 unsigned row = 0;
1656 u16 header_offset = 0;
1657 struct rb_node *nd;
1658 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1660 if (hb->show_headers) {
1661 hist_browser__show_headers(hb);
1662 header_offset = 1;
1665 ui_browser__hists_init_top(browser);
1666 hb->he_selection = NULL;
1667 hb->selection = NULL;
1669 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1670 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1671 float percent;
1673 if (h->filtered) {
1674 /* let it move to sibling */
1675 h->unfolded = false;
1676 continue;
1679 percent = hist_entry__get_percent_limit(h);
1680 if (percent < hb->min_pcnt)
1681 continue;
1683 if (symbol_conf.report_hierarchy) {
1684 row += hist_browser__show_hierarchy_entry(hb, h, row,
1685 h->depth);
1686 if (row == browser->rows)
1687 break;
1689 if (h->has_no_entry) {
1690 hist_browser__show_no_entry(hb, row, h->depth + 1);
1691 row++;
1693 } else {
1694 row += hist_browser__show_entry(hb, h, row);
1697 if (row == browser->rows)
1698 break;
1701 return row + header_offset;
1704 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1705 float min_pcnt)
1707 while (nd != NULL) {
1708 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1709 float percent = hist_entry__get_percent_limit(h);
1711 if (!h->filtered && percent >= min_pcnt)
1712 return nd;
1715 * If it's filtered, its all children also were filtered.
1716 * So move to sibling node.
1718 if (rb_next(nd))
1719 nd = rb_next(nd);
1720 else
1721 nd = rb_hierarchy_next(nd);
1724 return NULL;
1727 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1728 float min_pcnt)
1730 while (nd != NULL) {
1731 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1732 float percent = hist_entry__get_percent_limit(h);
1734 if (!h->filtered && percent >= min_pcnt)
1735 return nd;
1737 nd = rb_hierarchy_prev(nd);
1740 return NULL;
1743 static void ui_browser__hists_seek(struct ui_browser *browser,
1744 off_t offset, int whence)
1746 struct hist_entry *h;
1747 struct rb_node *nd;
1748 bool first = true;
1749 struct hist_browser *hb;
1751 hb = container_of(browser, struct hist_browser, b);
1753 if (browser->nr_entries == 0)
1754 return;
1756 ui_browser__hists_init_top(browser);
1758 switch (whence) {
1759 case SEEK_SET:
1760 nd = hists__filter_entries(rb_first(browser->entries),
1761 hb->min_pcnt);
1762 break;
1763 case SEEK_CUR:
1764 nd = browser->top;
1765 goto do_offset;
1766 case SEEK_END:
1767 nd = rb_hierarchy_last(rb_last(browser->entries));
1768 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
1769 first = false;
1770 break;
1771 default:
1772 return;
1776 * Moves not relative to the first visible entry invalidates its
1777 * row_offset:
1779 h = rb_entry(browser->top, struct hist_entry, rb_node);
1780 h->row_offset = 0;
1783 * Here we have to check if nd is expanded (+), if it is we can't go
1784 * the next top level hist_entry, instead we must compute an offset of
1785 * what _not_ to show and not change the first visible entry.
1787 * This offset increments when we are going from top to bottom and
1788 * decreases when we're going from bottom to top.
1790 * As we don't have backpointers to the top level in the callchains
1791 * structure, we need to always print the whole hist_entry callchain,
1792 * skipping the first ones that are before the first visible entry
1793 * and stop when we printed enough lines to fill the screen.
1795 do_offset:
1796 if (!nd)
1797 return;
1799 if (offset > 0) {
1800 do {
1801 h = rb_entry(nd, struct hist_entry, rb_node);
1802 if (h->unfolded && h->leaf) {
1803 u16 remaining = h->nr_rows - h->row_offset;
1804 if (offset > remaining) {
1805 offset -= remaining;
1806 h->row_offset = 0;
1807 } else {
1808 h->row_offset += offset;
1809 offset = 0;
1810 browser->top = nd;
1811 break;
1814 nd = hists__filter_entries(rb_hierarchy_next(nd),
1815 hb->min_pcnt);
1816 if (nd == NULL)
1817 break;
1818 --offset;
1819 browser->top = nd;
1820 } while (offset != 0);
1821 } else if (offset < 0) {
1822 while (1) {
1823 h = rb_entry(nd, struct hist_entry, rb_node);
1824 if (h->unfolded && h->leaf) {
1825 if (first) {
1826 if (-offset > h->row_offset) {
1827 offset += h->row_offset;
1828 h->row_offset = 0;
1829 } else {
1830 h->row_offset += offset;
1831 offset = 0;
1832 browser->top = nd;
1833 break;
1835 } else {
1836 if (-offset > h->nr_rows) {
1837 offset += h->nr_rows;
1838 h->row_offset = 0;
1839 } else {
1840 h->row_offset = h->nr_rows + offset;
1841 offset = 0;
1842 browser->top = nd;
1843 break;
1848 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
1849 hb->min_pcnt);
1850 if (nd == NULL)
1851 break;
1852 ++offset;
1853 browser->top = nd;
1854 if (offset == 0) {
1856 * Last unfiltered hist_entry, check if it is
1857 * unfolded, if it is then we should have
1858 * row_offset at its last entry.
1860 h = rb_entry(nd, struct hist_entry, rb_node);
1861 if (h->unfolded && h->leaf)
1862 h->row_offset = h->nr_rows;
1863 break;
1865 first = false;
1867 } else {
1868 browser->top = nd;
1869 h = rb_entry(nd, struct hist_entry, rb_node);
1870 h->row_offset = 0;
1874 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
1875 struct hist_entry *he, FILE *fp,
1876 int level)
1878 struct callchain_print_arg arg = {
1879 .fp = fp,
1882 hist_browser__show_callchain(browser, he, level, 0,
1883 hist_browser__fprintf_callchain_entry, &arg,
1884 hist_browser__check_dump_full);
1885 return arg.printed;
1888 static int hist_browser__fprintf_entry(struct hist_browser *browser,
1889 struct hist_entry *he, FILE *fp)
1891 char s[8192];
1892 int printed = 0;
1893 char folded_sign = ' ';
1894 struct perf_hpp hpp = {
1895 .buf = s,
1896 .size = sizeof(s),
1898 struct perf_hpp_fmt *fmt;
1899 bool first = true;
1900 int ret;
1902 if (symbol_conf.use_callchain) {
1903 folded_sign = hist_entry__folded(he);
1904 printed += fprintf(fp, "%c ", folded_sign);
1907 hists__for_each_format(browser->hists, fmt) {
1908 if (perf_hpp__should_skip(fmt, he->hists))
1909 continue;
1911 if (!first) {
1912 ret = scnprintf(hpp.buf, hpp.size, " ");
1913 advance_hpp(&hpp, ret);
1914 } else
1915 first = false;
1917 ret = fmt->entry(fmt, &hpp, he);
1918 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
1919 advance_hpp(&hpp, ret);
1921 printed += fprintf(fp, "%s\n", s);
1923 if (folded_sign == '-')
1924 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
1926 return printed;
1930 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
1931 struct hist_entry *he,
1932 FILE *fp, int level)
1934 char s[8192];
1935 int printed = 0;
1936 char folded_sign = ' ';
1937 struct perf_hpp hpp = {
1938 .buf = s,
1939 .size = sizeof(s),
1941 struct perf_hpp_fmt *fmt;
1942 struct perf_hpp_list_node *fmt_node;
1943 bool first = true;
1944 int ret;
1945 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1947 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
1949 folded_sign = hist_entry__folded(he);
1950 printed += fprintf(fp, "%c", folded_sign);
1952 /* the first hpp_list_node is for overhead columns */
1953 fmt_node = list_first_entry(&he->hists->hpp_formats,
1954 struct perf_hpp_list_node, list);
1955 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1956 if (!first) {
1957 ret = scnprintf(hpp.buf, hpp.size, " ");
1958 advance_hpp(&hpp, ret);
1959 } else
1960 first = false;
1962 ret = fmt->entry(fmt, &hpp, he);
1963 advance_hpp(&hpp, ret);
1966 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
1967 advance_hpp(&hpp, ret);
1969 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
1970 ret = scnprintf(hpp.buf, hpp.size, " ");
1971 advance_hpp(&hpp, ret);
1973 ret = fmt->entry(fmt, &hpp, he);
1974 advance_hpp(&hpp, ret);
1977 printed += fprintf(fp, "%s\n", rtrim(s));
1979 if (he->leaf && folded_sign == '-') {
1980 printed += hist_browser__fprintf_callchain(browser, he, fp,
1981 he->depth + 1);
1984 return printed;
1987 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
1989 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
1990 browser->min_pcnt);
1991 int printed = 0;
1993 while (nd) {
1994 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1996 if (symbol_conf.report_hierarchy) {
1997 printed += hist_browser__fprintf_hierarchy_entry(browser,
1998 h, fp,
1999 h->depth);
2000 } else {
2001 printed += hist_browser__fprintf_entry(browser, h, fp);
2004 nd = hists__filter_entries(rb_hierarchy_next(nd),
2005 browser->min_pcnt);
2008 return printed;
2011 static int hist_browser__dump(struct hist_browser *browser)
2013 char filename[64];
2014 FILE *fp;
2016 while (1) {
2017 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2018 if (access(filename, F_OK))
2019 break;
2021 * XXX: Just an arbitrary lazy upper limit
2023 if (++browser->print_seq == 8192) {
2024 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2025 return -1;
2029 fp = fopen(filename, "w");
2030 if (fp == NULL) {
2031 char bf[64];
2032 const char *err = str_error_r(errno, bf, sizeof(bf));
2033 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2034 return -1;
2037 ++browser->print_seq;
2038 hist_browser__fprintf(browser, fp);
2039 fclose(fp);
2040 ui_helpline__fpush("%s written!", filename);
2042 return 0;
2045 void hist_browser__init(struct hist_browser *browser,
2046 struct hists *hists)
2048 struct perf_hpp_fmt *fmt;
2050 browser->hists = hists;
2051 browser->b.refresh = hist_browser__refresh;
2052 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2053 browser->b.seek = ui_browser__hists_seek;
2054 browser->b.use_navkeypressed = true;
2055 browser->show_headers = symbol_conf.show_hist_headers;
2057 hists__for_each_format(hists, fmt) {
2058 perf_hpp__reset_width(fmt, hists);
2059 ++browser->b.columns;
2063 struct hist_browser *hist_browser__new(struct hists *hists)
2065 struct hist_browser *browser = zalloc(sizeof(*browser));
2067 if (browser)
2068 hist_browser__init(browser, hists);
2070 return browser;
2073 static struct hist_browser *
2074 perf_evsel_browser__new(struct perf_evsel *evsel,
2075 struct hist_browser_timer *hbt,
2076 struct perf_env *env)
2078 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2080 if (browser) {
2081 browser->hbt = hbt;
2082 browser->env = env;
2083 browser->title = perf_evsel_browser_title;
2085 return browser;
2088 void hist_browser__delete(struct hist_browser *browser)
2090 free(browser);
2093 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2095 return browser->he_selection;
2098 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2100 return browser->he_selection->thread;
2103 /* Check whether the browser is for 'top' or 'report' */
2104 static inline bool is_report_browser(void *timer)
2106 return timer == NULL;
2109 static int perf_evsel_browser_title(struct hist_browser *browser,
2110 char *bf, size_t size)
2112 struct hist_browser_timer *hbt = browser->hbt;
2113 struct hists *hists = browser->hists;
2114 char unit;
2115 int printed;
2116 const struct dso *dso = hists->dso_filter;
2117 const struct thread *thread = hists->thread_filter;
2118 int socket_id = hists->socket_filter;
2119 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2120 u64 nr_events = hists->stats.total_period;
2121 struct perf_evsel *evsel = hists_to_evsel(hists);
2122 const char *ev_name = perf_evsel__name(evsel);
2123 char buf[512];
2124 size_t buflen = sizeof(buf);
2125 char ref[30] = " show reference callgraph, ";
2126 bool enable_ref = false;
2128 if (symbol_conf.filter_relative) {
2129 nr_samples = hists->stats.nr_non_filtered_samples;
2130 nr_events = hists->stats.total_non_filtered_period;
2133 if (perf_evsel__is_group_event(evsel)) {
2134 struct perf_evsel *pos;
2136 perf_evsel__group_desc(evsel, buf, buflen);
2137 ev_name = buf;
2139 for_each_group_member(pos, evsel) {
2140 struct hists *pos_hists = evsel__hists(pos);
2142 if (symbol_conf.filter_relative) {
2143 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2144 nr_events += pos_hists->stats.total_non_filtered_period;
2145 } else {
2146 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2147 nr_events += pos_hists->stats.total_period;
2152 if (symbol_conf.show_ref_callgraph &&
2153 strstr(ev_name, "call-graph=no"))
2154 enable_ref = true;
2155 nr_samples = convert_unit(nr_samples, &unit);
2156 printed = scnprintf(bf, size,
2157 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2158 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2161 if (hists->uid_filter_str)
2162 printed += snprintf(bf + printed, size - printed,
2163 ", UID: %s", hists->uid_filter_str);
2164 if (thread) {
2165 if (hists__has(hists, thread)) {
2166 printed += scnprintf(bf + printed, size - printed,
2167 ", Thread: %s(%d)",
2168 (thread->comm_set ? thread__comm_str(thread) : ""),
2169 thread->tid);
2170 } else {
2171 printed += scnprintf(bf + printed, size - printed,
2172 ", Thread: %s",
2173 (thread->comm_set ? thread__comm_str(thread) : ""));
2176 if (dso)
2177 printed += scnprintf(bf + printed, size - printed,
2178 ", DSO: %s", dso->short_name);
2179 if (socket_id > -1)
2180 printed += scnprintf(bf + printed, size - printed,
2181 ", Processor Socket: %d", socket_id);
2182 if (!is_report_browser(hbt)) {
2183 struct perf_top *top = hbt->arg;
2185 if (top->zero)
2186 printed += scnprintf(bf + printed, size - printed, " [z]");
2189 return printed;
2192 static inline void free_popup_options(char **options, int n)
2194 int i;
2196 for (i = 0; i < n; ++i)
2197 zfree(&options[i]);
2201 * Only runtime switching of perf data file will make "input_name" point
2202 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2203 * whether we need to call free() for current "input_name" during the switch.
2205 static bool is_input_name_malloced = false;
2207 static int switch_data_file(void)
2209 char *pwd, *options[32], *abs_path[32], *tmp;
2210 DIR *pwd_dir;
2211 int nr_options = 0, choice = -1, ret = -1;
2212 struct dirent *dent;
2214 pwd = getenv("PWD");
2215 if (!pwd)
2216 return ret;
2218 pwd_dir = opendir(pwd);
2219 if (!pwd_dir)
2220 return ret;
2222 memset(options, 0, sizeof(options));
2223 memset(options, 0, sizeof(abs_path));
2225 while ((dent = readdir(pwd_dir))) {
2226 char path[PATH_MAX];
2227 u64 magic;
2228 char *name = dent->d_name;
2229 FILE *file;
2231 if (!(dent->d_type == DT_REG))
2232 continue;
2234 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2236 file = fopen(path, "r");
2237 if (!file)
2238 continue;
2240 if (fread(&magic, 1, 8, file) < 8)
2241 goto close_file_and_continue;
2243 if (is_perf_magic(magic)) {
2244 options[nr_options] = strdup(name);
2245 if (!options[nr_options])
2246 goto close_file_and_continue;
2248 abs_path[nr_options] = strdup(path);
2249 if (!abs_path[nr_options]) {
2250 zfree(&options[nr_options]);
2251 ui__warning("Can't search all data files due to memory shortage.\n");
2252 fclose(file);
2253 break;
2256 nr_options++;
2259 close_file_and_continue:
2260 fclose(file);
2261 if (nr_options >= 32) {
2262 ui__warning("Too many perf data files in PWD!\n"
2263 "Only the first 32 files will be listed.\n");
2264 break;
2267 closedir(pwd_dir);
2269 if (nr_options) {
2270 choice = ui__popup_menu(nr_options, options);
2271 if (choice < nr_options && choice >= 0) {
2272 tmp = strdup(abs_path[choice]);
2273 if (tmp) {
2274 if (is_input_name_malloced)
2275 free((void *)input_name);
2276 input_name = tmp;
2277 is_input_name_malloced = true;
2278 ret = 0;
2279 } else
2280 ui__warning("Data switch failed due to memory shortage!\n");
2284 free_popup_options(options, nr_options);
2285 free_popup_options(abs_path, nr_options);
2286 return ret;
2289 struct popup_action {
2290 struct thread *thread;
2291 struct map_symbol ms;
2292 int socket;
2294 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2297 static int
2298 do_annotate(struct hist_browser *browser, struct popup_action *act)
2300 struct perf_evsel *evsel;
2301 struct annotation *notes;
2302 struct hist_entry *he;
2303 int err;
2305 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2306 return 0;
2308 notes = symbol__annotation(act->ms.sym);
2309 if (!notes->src)
2310 return 0;
2312 evsel = hists_to_evsel(browser->hists);
2313 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2314 he = hist_browser__selected_entry(browser);
2316 * offer option to annotate the other branch source or target
2317 * (if they exists) when returning from annotate
2319 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2320 return 1;
2322 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2323 if (err)
2324 ui_browser__handle_resize(&browser->b);
2325 return 0;
2328 static int
2329 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2330 struct popup_action *act, char **optstr,
2331 struct map *map, struct symbol *sym)
2333 if (sym == NULL || map->dso->annotate_warned)
2334 return 0;
2336 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2337 return 0;
2339 act->ms.map = map;
2340 act->ms.sym = sym;
2341 act->fn = do_annotate;
2342 return 1;
2345 static int
2346 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2348 struct thread *thread = act->thread;
2350 if ((!hists__has(browser->hists, thread) &&
2351 !hists__has(browser->hists, comm)) || thread == NULL)
2352 return 0;
2354 if (browser->hists->thread_filter) {
2355 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2356 perf_hpp__set_elide(HISTC_THREAD, false);
2357 thread__zput(browser->hists->thread_filter);
2358 ui_helpline__pop();
2359 } else {
2360 if (hists__has(browser->hists, thread)) {
2361 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2362 thread->comm_set ? thread__comm_str(thread) : "",
2363 thread->tid);
2364 } else {
2365 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2366 thread->comm_set ? thread__comm_str(thread) : "");
2369 browser->hists->thread_filter = thread__get(thread);
2370 perf_hpp__set_elide(HISTC_THREAD, false);
2371 pstack__push(browser->pstack, &browser->hists->thread_filter);
2374 hists__filter_by_thread(browser->hists);
2375 hist_browser__reset(browser);
2376 return 0;
2379 static int
2380 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2381 char **optstr, struct thread *thread)
2383 int ret;
2385 if ((!hists__has(browser->hists, thread) &&
2386 !hists__has(browser->hists, comm)) || thread == NULL)
2387 return 0;
2389 if (hists__has(browser->hists, thread)) {
2390 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2391 browser->hists->thread_filter ? "out of" : "into",
2392 thread->comm_set ? thread__comm_str(thread) : "",
2393 thread->tid);
2394 } else {
2395 ret = asprintf(optstr, "Zoom %s %s thread",
2396 browser->hists->thread_filter ? "out of" : "into",
2397 thread->comm_set ? thread__comm_str(thread) : "");
2399 if (ret < 0)
2400 return 0;
2402 act->thread = thread;
2403 act->fn = do_zoom_thread;
2404 return 1;
2407 static int
2408 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2410 struct map *map = act->ms.map;
2412 if (!hists__has(browser->hists, dso) || map == NULL)
2413 return 0;
2415 if (browser->hists->dso_filter) {
2416 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2417 perf_hpp__set_elide(HISTC_DSO, false);
2418 browser->hists->dso_filter = NULL;
2419 ui_helpline__pop();
2420 } else {
2421 if (map == NULL)
2422 return 0;
2423 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2424 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2425 browser->hists->dso_filter = map->dso;
2426 perf_hpp__set_elide(HISTC_DSO, true);
2427 pstack__push(browser->pstack, &browser->hists->dso_filter);
2430 hists__filter_by_dso(browser->hists);
2431 hist_browser__reset(browser);
2432 return 0;
2435 static int
2436 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2437 char **optstr, struct map *map)
2439 if (!hists__has(browser->hists, dso) || map == NULL)
2440 return 0;
2442 if (asprintf(optstr, "Zoom %s %s DSO",
2443 browser->hists->dso_filter ? "out of" : "into",
2444 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2445 return 0;
2447 act->ms.map = map;
2448 act->fn = do_zoom_dso;
2449 return 1;
2452 static int
2453 do_browse_map(struct hist_browser *browser __maybe_unused,
2454 struct popup_action *act)
2456 map__browse(act->ms.map);
2457 return 0;
2460 static int
2461 add_map_opt(struct hist_browser *browser,
2462 struct popup_action *act, char **optstr, struct map *map)
2464 if (!hists__has(browser->hists, dso) || map == NULL)
2465 return 0;
2467 if (asprintf(optstr, "Browse map details") < 0)
2468 return 0;
2470 act->ms.map = map;
2471 act->fn = do_browse_map;
2472 return 1;
2475 static int
2476 do_run_script(struct hist_browser *browser __maybe_unused,
2477 struct popup_action *act)
2479 char script_opt[64];
2480 memset(script_opt, 0, sizeof(script_opt));
2482 if (act->thread) {
2483 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2484 thread__comm_str(act->thread));
2485 } else if (act->ms.sym) {
2486 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2487 act->ms.sym->name);
2490 script_browse(script_opt);
2491 return 0;
2494 static int
2495 add_script_opt(struct hist_browser *browser __maybe_unused,
2496 struct popup_action *act, char **optstr,
2497 struct thread *thread, struct symbol *sym)
2499 if (thread) {
2500 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2501 thread__comm_str(thread)) < 0)
2502 return 0;
2503 } else if (sym) {
2504 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2505 sym->name) < 0)
2506 return 0;
2507 } else {
2508 if (asprintf(optstr, "Run scripts for all samples") < 0)
2509 return 0;
2512 act->thread = thread;
2513 act->ms.sym = sym;
2514 act->fn = do_run_script;
2515 return 1;
2518 static int
2519 do_switch_data(struct hist_browser *browser __maybe_unused,
2520 struct popup_action *act __maybe_unused)
2522 if (switch_data_file()) {
2523 ui__warning("Won't switch the data files due to\n"
2524 "no valid data file get selected!\n");
2525 return 0;
2528 return K_SWITCH_INPUT_DATA;
2531 static int
2532 add_switch_opt(struct hist_browser *browser,
2533 struct popup_action *act, char **optstr)
2535 if (!is_report_browser(browser->hbt))
2536 return 0;
2538 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2539 return 0;
2541 act->fn = do_switch_data;
2542 return 1;
2545 static int
2546 do_exit_browser(struct hist_browser *browser __maybe_unused,
2547 struct popup_action *act __maybe_unused)
2549 return 0;
2552 static int
2553 add_exit_opt(struct hist_browser *browser __maybe_unused,
2554 struct popup_action *act, char **optstr)
2556 if (asprintf(optstr, "Exit") < 0)
2557 return 0;
2559 act->fn = do_exit_browser;
2560 return 1;
2563 static int
2564 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2566 if (!hists__has(browser->hists, socket) || act->socket < 0)
2567 return 0;
2569 if (browser->hists->socket_filter > -1) {
2570 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2571 browser->hists->socket_filter = -1;
2572 perf_hpp__set_elide(HISTC_SOCKET, false);
2573 } else {
2574 browser->hists->socket_filter = act->socket;
2575 perf_hpp__set_elide(HISTC_SOCKET, true);
2576 pstack__push(browser->pstack, &browser->hists->socket_filter);
2579 hists__filter_by_socket(browser->hists);
2580 hist_browser__reset(browser);
2581 return 0;
2584 static int
2585 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2586 char **optstr, int socket_id)
2588 if (!hists__has(browser->hists, socket) || socket_id < 0)
2589 return 0;
2591 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2592 (browser->hists->socket_filter > -1) ? "out of" : "into",
2593 socket_id) < 0)
2594 return 0;
2596 act->socket = socket_id;
2597 act->fn = do_zoom_socket;
2598 return 1;
2601 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2603 u64 nr_entries = 0;
2604 struct rb_node *nd = rb_first(&hb->hists->entries);
2606 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2607 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2608 return;
2611 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2612 nr_entries++;
2613 nd = rb_hierarchy_next(nd);
2616 hb->nr_non_filtered_entries = nr_entries;
2617 hb->nr_hierarchy_entries = nr_entries;
2620 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2621 double percent)
2623 struct hist_entry *he;
2624 struct rb_node *nd = rb_first(&hb->hists->entries);
2625 u64 total = hists__total_period(hb->hists);
2626 u64 min_callchain_hits = total * (percent / 100);
2628 hb->min_pcnt = callchain_param.min_percent = percent;
2630 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2631 he = rb_entry(nd, struct hist_entry, rb_node);
2633 if (he->has_no_entry) {
2634 he->has_no_entry = false;
2635 he->nr_rows = 0;
2638 if (!he->leaf || !symbol_conf.use_callchain)
2639 goto next;
2641 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2642 total = he->stat.period;
2644 if (symbol_conf.cumulate_callchain)
2645 total = he->stat_acc->period;
2647 min_callchain_hits = total * (percent / 100);
2650 callchain_param.sort(&he->sorted_chain, he->callchain,
2651 min_callchain_hits, &callchain_param);
2653 next:
2654 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2656 /* force to re-evaluate folding state of callchains */
2657 he->init_have_children = false;
2658 hist_entry__set_folding(he, hb, false);
2662 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2663 const char *helpline,
2664 bool left_exits,
2665 struct hist_browser_timer *hbt,
2666 float min_pcnt,
2667 struct perf_env *env)
2669 struct hists *hists = evsel__hists(evsel);
2670 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2671 struct branch_info *bi;
2672 #define MAX_OPTIONS 16
2673 char *options[MAX_OPTIONS];
2674 struct popup_action actions[MAX_OPTIONS];
2675 int nr_options = 0;
2676 int key = -1;
2677 char buf[64];
2678 int delay_secs = hbt ? hbt->refresh : 0;
2680 #define HIST_BROWSER_HELP_COMMON \
2681 "h/?/F1 Show this window\n" \
2682 "UP/DOWN/PGUP\n" \
2683 "PGDN/SPACE Navigate\n" \
2684 "q/ESC/CTRL+C Exit browser\n\n" \
2685 "For multiple event sessions:\n\n" \
2686 "TAB/UNTAB Switch events\n\n" \
2687 "For symbolic views (--sort has sym):\n\n" \
2688 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2689 "ESC Zoom out\n" \
2690 "a Annotate current symbol\n" \
2691 "C Collapse all callchains\n" \
2692 "d Zoom into current DSO\n" \
2693 "E Expand all callchains\n" \
2694 "F Toggle percentage of filtered entries\n" \
2695 "H Display column headers\n" \
2696 "L Change percent limit\n" \
2697 "m Display context menu\n" \
2698 "S Zoom into current Processor Socket\n" \
2700 /* help messages are sorted by lexical order of the hotkey */
2701 const char report_help[] = HIST_BROWSER_HELP_COMMON
2702 "i Show header information\n"
2703 "P Print histograms to perf.hist.N\n"
2704 "r Run available scripts\n"
2705 "s Switch to another data file in PWD\n"
2706 "t Zoom into current Thread\n"
2707 "V Verbose (DSO names in callchains, etc)\n"
2708 "/ Filter symbol by name";
2709 const char top_help[] = HIST_BROWSER_HELP_COMMON
2710 "P Print histograms to perf.hist.N\n"
2711 "t Zoom into current Thread\n"
2712 "V Verbose (DSO names in callchains, etc)\n"
2713 "z Toggle zeroing of samples\n"
2714 "f Enable/Disable events\n"
2715 "/ Filter symbol by name";
2717 if (browser == NULL)
2718 return -1;
2720 /* reset abort key so that it can get Ctrl-C as a key */
2721 SLang_reset_tty();
2722 SLang_init_tty(0, 0, 0);
2724 if (min_pcnt)
2725 browser->min_pcnt = min_pcnt;
2726 hist_browser__update_nr_entries(browser);
2728 browser->pstack = pstack__new(3);
2729 if (browser->pstack == NULL)
2730 goto out;
2732 ui_helpline__push(helpline);
2734 memset(options, 0, sizeof(options));
2735 memset(actions, 0, sizeof(actions));
2737 if (symbol_conf.col_width_list_str)
2738 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2740 while (1) {
2741 struct thread *thread = NULL;
2742 struct map *map = NULL;
2743 int choice = 0;
2744 int socked_id = -1;
2746 nr_options = 0;
2748 key = hist_browser__run(browser, helpline);
2750 if (browser->he_selection != NULL) {
2751 thread = hist_browser__selected_thread(browser);
2752 map = browser->selection->map;
2753 socked_id = browser->he_selection->socket;
2755 switch (key) {
2756 case K_TAB:
2757 case K_UNTAB:
2758 if (nr_events == 1)
2759 continue;
2761 * Exit the browser, let hists__browser_tree
2762 * go to the next or previous
2764 goto out_free_stack;
2765 case 'a':
2766 if (!hists__has(hists, sym)) {
2767 ui_browser__warning(&browser->b, delay_secs * 2,
2768 "Annotation is only available for symbolic views, "
2769 "include \"sym*\" in --sort to use it.");
2770 continue;
2773 if (browser->selection == NULL ||
2774 browser->selection->sym == NULL ||
2775 browser->selection->map->dso->annotate_warned)
2776 continue;
2778 actions->ms.map = browser->selection->map;
2779 actions->ms.sym = browser->selection->sym;
2780 do_annotate(browser, actions);
2781 continue;
2782 case 'P':
2783 hist_browser__dump(browser);
2784 continue;
2785 case 'd':
2786 actions->ms.map = map;
2787 do_zoom_dso(browser, actions);
2788 continue;
2789 case 'V':
2790 browser->show_dso = !browser->show_dso;
2791 continue;
2792 case 't':
2793 actions->thread = thread;
2794 do_zoom_thread(browser, actions);
2795 continue;
2796 case 'S':
2797 actions->socket = socked_id;
2798 do_zoom_socket(browser, actions);
2799 continue;
2800 case '/':
2801 if (ui_browser__input_window("Symbol to show",
2802 "Please enter the name of symbol you want to see.\n"
2803 "To remove the filter later, press / + ENTER.",
2804 buf, "ENTER: OK, ESC: Cancel",
2805 delay_secs * 2) == K_ENTER) {
2806 hists->symbol_filter_str = *buf ? buf : NULL;
2807 hists__filter_by_symbol(hists);
2808 hist_browser__reset(browser);
2810 continue;
2811 case 'r':
2812 if (is_report_browser(hbt)) {
2813 actions->thread = NULL;
2814 actions->ms.sym = NULL;
2815 do_run_script(browser, actions);
2817 continue;
2818 case 's':
2819 if (is_report_browser(hbt)) {
2820 key = do_switch_data(browser, actions);
2821 if (key == K_SWITCH_INPUT_DATA)
2822 goto out_free_stack;
2824 continue;
2825 case 'i':
2826 /* env->arch is NULL for live-mode (i.e. perf top) */
2827 if (env->arch)
2828 tui__header_window(env);
2829 continue;
2830 case 'F':
2831 symbol_conf.filter_relative ^= 1;
2832 continue;
2833 case 'z':
2834 if (!is_report_browser(hbt)) {
2835 struct perf_top *top = hbt->arg;
2837 top->zero = !top->zero;
2839 continue;
2840 case 'L':
2841 if (ui_browser__input_window("Percent Limit",
2842 "Please enter the value you want to hide entries under that percent.",
2843 buf, "ENTER: OK, ESC: Cancel",
2844 delay_secs * 2) == K_ENTER) {
2845 char *end;
2846 double new_percent = strtod(buf, &end);
2848 if (new_percent < 0 || new_percent > 100) {
2849 ui_browser__warning(&browser->b, delay_secs * 2,
2850 "Invalid percent: %.2f", new_percent);
2851 continue;
2854 hist_browser__update_percent_limit(browser, new_percent);
2855 hist_browser__reset(browser);
2857 continue;
2858 case K_F1:
2859 case 'h':
2860 case '?':
2861 ui_browser__help_window(&browser->b,
2862 is_report_browser(hbt) ? report_help : top_help);
2863 continue;
2864 case K_ENTER:
2865 case K_RIGHT:
2866 case 'm':
2867 /* menu */
2868 break;
2869 case K_ESC:
2870 case K_LEFT: {
2871 const void *top;
2873 if (pstack__empty(browser->pstack)) {
2875 * Go back to the perf_evsel_menu__run or other user
2877 if (left_exits)
2878 goto out_free_stack;
2880 if (key == K_ESC &&
2881 ui_browser__dialog_yesno(&browser->b,
2882 "Do you really want to exit?"))
2883 goto out_free_stack;
2885 continue;
2887 top = pstack__peek(browser->pstack);
2888 if (top == &browser->hists->dso_filter) {
2890 * No need to set actions->dso here since
2891 * it's just to remove the current filter.
2892 * Ditto for thread below.
2894 do_zoom_dso(browser, actions);
2895 } else if (top == &browser->hists->thread_filter) {
2896 do_zoom_thread(browser, actions);
2897 } else if (top == &browser->hists->socket_filter) {
2898 do_zoom_socket(browser, actions);
2900 continue;
2902 case 'q':
2903 case CTRL('c'):
2904 goto out_free_stack;
2905 case 'f':
2906 if (!is_report_browser(hbt)) {
2907 struct perf_top *top = hbt->arg;
2909 perf_evlist__toggle_enable(top->evlist);
2911 * No need to refresh, resort/decay histogram
2912 * entries if we are not collecting samples:
2914 if (top->evlist->enabled) {
2915 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
2916 hbt->refresh = delay_secs;
2917 } else {
2918 helpline = "Press 'f' again to re-enable the events";
2919 hbt->refresh = 0;
2921 continue;
2923 /* Fall thru */
2924 default:
2925 helpline = "Press '?' for help on key bindings";
2926 continue;
2929 if (!hists__has(hists, sym) || browser->selection == NULL)
2930 goto skip_annotation;
2932 if (sort__mode == SORT_MODE__BRANCH) {
2933 bi = browser->he_selection->branch_info;
2935 if (bi == NULL)
2936 goto skip_annotation;
2938 nr_options += add_annotate_opt(browser,
2939 &actions[nr_options],
2940 &options[nr_options],
2941 bi->from.map,
2942 bi->from.sym);
2943 if (bi->to.sym != bi->from.sym)
2944 nr_options += add_annotate_opt(browser,
2945 &actions[nr_options],
2946 &options[nr_options],
2947 bi->to.map,
2948 bi->to.sym);
2949 } else {
2950 nr_options += add_annotate_opt(browser,
2951 &actions[nr_options],
2952 &options[nr_options],
2953 browser->selection->map,
2954 browser->selection->sym);
2956 skip_annotation:
2957 nr_options += add_thread_opt(browser, &actions[nr_options],
2958 &options[nr_options], thread);
2959 nr_options += add_dso_opt(browser, &actions[nr_options],
2960 &options[nr_options], map);
2961 nr_options += add_map_opt(browser, &actions[nr_options],
2962 &options[nr_options],
2963 browser->selection ?
2964 browser->selection->map : NULL);
2965 nr_options += add_socket_opt(browser, &actions[nr_options],
2966 &options[nr_options],
2967 socked_id);
2968 /* perf script support */
2969 if (!is_report_browser(hbt))
2970 goto skip_scripting;
2972 if (browser->he_selection) {
2973 if (hists__has(hists, thread) && thread) {
2974 nr_options += add_script_opt(browser,
2975 &actions[nr_options],
2976 &options[nr_options],
2977 thread, NULL);
2980 * Note that browser->selection != NULL
2981 * when browser->he_selection is not NULL,
2982 * so we don't need to check browser->selection
2983 * before fetching browser->selection->sym like what
2984 * we do before fetching browser->selection->map.
2986 * See hist_browser__show_entry.
2988 if (hists__has(hists, sym) && browser->selection->sym) {
2989 nr_options += add_script_opt(browser,
2990 &actions[nr_options],
2991 &options[nr_options],
2992 NULL, browser->selection->sym);
2995 nr_options += add_script_opt(browser, &actions[nr_options],
2996 &options[nr_options], NULL, NULL);
2997 nr_options += add_switch_opt(browser, &actions[nr_options],
2998 &options[nr_options]);
2999 skip_scripting:
3000 nr_options += add_exit_opt(browser, &actions[nr_options],
3001 &options[nr_options]);
3003 do {
3004 struct popup_action *act;
3006 choice = ui__popup_menu(nr_options, options);
3007 if (choice == -1 || choice >= nr_options)
3008 break;
3010 act = &actions[choice];
3011 key = act->fn(browser, act);
3012 } while (key == 1);
3014 if (key == K_SWITCH_INPUT_DATA)
3015 break;
3017 out_free_stack:
3018 pstack__delete(browser->pstack);
3019 out:
3020 hist_browser__delete(browser);
3021 free_popup_options(options, MAX_OPTIONS);
3022 return key;
3025 struct perf_evsel_menu {
3026 struct ui_browser b;
3027 struct perf_evsel *selection;
3028 bool lost_events, lost_events_warned;
3029 float min_pcnt;
3030 struct perf_env *env;
3033 static void perf_evsel_menu__write(struct ui_browser *browser,
3034 void *entry, int row)
3036 struct perf_evsel_menu *menu = container_of(browser,
3037 struct perf_evsel_menu, b);
3038 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3039 struct hists *hists = evsel__hists(evsel);
3040 bool current_entry = ui_browser__is_current_entry(browser, row);
3041 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3042 const char *ev_name = perf_evsel__name(evsel);
3043 char bf[256], unit;
3044 const char *warn = " ";
3045 size_t printed;
3047 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3048 HE_COLORSET_NORMAL);
3050 if (perf_evsel__is_group_event(evsel)) {
3051 struct perf_evsel *pos;
3053 ev_name = perf_evsel__group_name(evsel);
3055 for_each_group_member(pos, evsel) {
3056 struct hists *pos_hists = evsel__hists(pos);
3057 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3061 nr_events = convert_unit(nr_events, &unit);
3062 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3063 unit, unit == ' ' ? "" : " ", ev_name);
3064 ui_browser__printf(browser, "%s", bf);
3066 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3067 if (nr_events != 0) {
3068 menu->lost_events = true;
3069 if (!current_entry)
3070 ui_browser__set_color(browser, HE_COLORSET_TOP);
3071 nr_events = convert_unit(nr_events, &unit);
3072 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3073 nr_events, unit, unit == ' ' ? "" : " ");
3074 warn = bf;
3077 ui_browser__write_nstring(browser, warn, browser->width - printed);
3079 if (current_entry)
3080 menu->selection = evsel;
3083 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3084 int nr_events, const char *help,
3085 struct hist_browser_timer *hbt)
3087 struct perf_evlist *evlist = menu->b.priv;
3088 struct perf_evsel *pos;
3089 const char *title = "Available samples";
3090 int delay_secs = hbt ? hbt->refresh : 0;
3091 int key;
3093 if (ui_browser__show(&menu->b, title,
3094 "ESC: exit, ENTER|->: Browse histograms") < 0)
3095 return -1;
3097 while (1) {
3098 key = ui_browser__run(&menu->b, delay_secs);
3100 switch (key) {
3101 case K_TIMER:
3102 hbt->timer(hbt->arg);
3104 if (!menu->lost_events_warned && menu->lost_events) {
3105 ui_browser__warn_lost_events(&menu->b);
3106 menu->lost_events_warned = true;
3108 continue;
3109 case K_RIGHT:
3110 case K_ENTER:
3111 if (!menu->selection)
3112 continue;
3113 pos = menu->selection;
3114 browse_hists:
3115 perf_evlist__set_selected(evlist, pos);
3117 * Give the calling tool a chance to populate the non
3118 * default evsel resorted hists tree.
3120 if (hbt)
3121 hbt->timer(hbt->arg);
3122 key = perf_evsel__hists_browse(pos, nr_events, help,
3123 true, hbt,
3124 menu->min_pcnt,
3125 menu->env);
3126 ui_browser__show_title(&menu->b, title);
3127 switch (key) {
3128 case K_TAB:
3129 if (pos->node.next == &evlist->entries)
3130 pos = perf_evlist__first(evlist);
3131 else
3132 pos = perf_evsel__next(pos);
3133 goto browse_hists;
3134 case K_UNTAB:
3135 if (pos->node.prev == &evlist->entries)
3136 pos = perf_evlist__last(evlist);
3137 else
3138 pos = perf_evsel__prev(pos);
3139 goto browse_hists;
3140 case K_SWITCH_INPUT_DATA:
3141 case 'q':
3142 case CTRL('c'):
3143 goto out;
3144 case K_ESC:
3145 default:
3146 continue;
3148 case K_LEFT:
3149 continue;
3150 case K_ESC:
3151 if (!ui_browser__dialog_yesno(&menu->b,
3152 "Do you really want to exit?"))
3153 continue;
3154 /* Fall thru */
3155 case 'q':
3156 case CTRL('c'):
3157 goto out;
3158 default:
3159 continue;
3163 out:
3164 ui_browser__hide(&menu->b);
3165 return key;
3168 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3169 void *entry)
3171 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3173 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3174 return true;
3176 return false;
3179 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3180 int nr_entries, const char *help,
3181 struct hist_browser_timer *hbt,
3182 float min_pcnt,
3183 struct perf_env *env)
3185 struct perf_evsel *pos;
3186 struct perf_evsel_menu menu = {
3187 .b = {
3188 .entries = &evlist->entries,
3189 .refresh = ui_browser__list_head_refresh,
3190 .seek = ui_browser__list_head_seek,
3191 .write = perf_evsel_menu__write,
3192 .filter = filter_group_entries,
3193 .nr_entries = nr_entries,
3194 .priv = evlist,
3196 .min_pcnt = min_pcnt,
3197 .env = env,
3200 ui_helpline__push("Press ESC to exit");
3202 evlist__for_each_entry(evlist, pos) {
3203 const char *ev_name = perf_evsel__name(pos);
3204 size_t line_len = strlen(ev_name) + 7;
3206 if (menu.b.width < line_len)
3207 menu.b.width = line_len;
3210 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3213 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3214 struct hist_browser_timer *hbt,
3215 float min_pcnt,
3216 struct perf_env *env)
3218 int nr_entries = evlist->nr_entries;
3220 single_entry:
3221 if (nr_entries == 1) {
3222 struct perf_evsel *first = perf_evlist__first(evlist);
3224 return perf_evsel__hists_browse(first, nr_entries, help,
3225 false, hbt, min_pcnt,
3226 env);
3229 if (symbol_conf.event_group) {
3230 struct perf_evsel *pos;
3232 nr_entries = 0;
3233 evlist__for_each_entry(evlist, pos) {
3234 if (perf_evsel__is_group_leader(pos))
3235 nr_entries++;
3238 if (nr_entries == 1)
3239 goto single_entry;
3242 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3243 hbt, min_pcnt, env);