License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[linux-2.6/btrfs-unstable.git] / tools / perf / ui / browsers / hists.c
blob628ad5f7eddb6a4295ba9370f987e9194dfe6bed
1 // SPDX-License-Identifier: GPL-2.0
2 #include <dirent.h>
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <linux/rbtree.h>
9 #include <sys/ttydefaults.h>
11 #include "../../util/evsel.h"
12 #include "../../util/evlist.h"
13 #include "../../util/hist.h"
14 #include "../../util/pstack.h"
15 #include "../../util/sort.h"
16 #include "../../util/util.h"
17 #include "../../util/top.h"
18 #include "../../util/thread.h"
19 #include "../../arch/common.h"
21 #include "../browsers/hists.h"
22 #include "../helpline.h"
23 #include "../util.h"
24 #include "../ui.h"
25 #include "map.h"
26 #include "annotate.h"
27 #include "srcline.h"
28 #include "string2.h"
29 #include "units.h"
31 #include "sane_ctype.h"
33 extern void hist_browser__init_hpp(void);
35 static int perf_evsel_browser_title(struct hist_browser *browser,
36 char *bf, size_t size);
37 static void hist_browser__update_nr_entries(struct hist_browser *hb);
39 static struct rb_node *hists__filter_entries(struct rb_node *nd,
40 float min_pcnt);
42 static bool hist_browser__has_filter(struct hist_browser *hb)
44 return hists__has_filter(hb->hists) || hb->min_pcnt || symbol_conf.has_filter || hb->c2c_filter;
47 static int hist_browser__get_folding(struct hist_browser *browser)
49 struct rb_node *nd;
50 struct hists *hists = browser->hists;
51 int unfolded_rows = 0;
53 for (nd = rb_first(&hists->entries);
54 (nd = hists__filter_entries(nd, browser->min_pcnt)) != NULL;
55 nd = rb_hierarchy_next(nd)) {
56 struct hist_entry *he =
57 rb_entry(nd, struct hist_entry, rb_node);
59 if (he->leaf && he->unfolded)
60 unfolded_rows += he->nr_rows;
62 return unfolded_rows;
65 static u32 hist_browser__nr_entries(struct hist_browser *hb)
67 u32 nr_entries;
69 if (symbol_conf.report_hierarchy)
70 nr_entries = hb->nr_hierarchy_entries;
71 else if (hist_browser__has_filter(hb))
72 nr_entries = hb->nr_non_filtered_entries;
73 else
74 nr_entries = hb->hists->nr_entries;
76 hb->nr_callchain_rows = hist_browser__get_folding(hb);
77 return nr_entries + hb->nr_callchain_rows;
80 static void hist_browser__update_rows(struct hist_browser *hb)
82 struct ui_browser *browser = &hb->b;
83 struct hists *hists = hb->hists;
84 struct perf_hpp_list *hpp_list = hists->hpp_list;
85 u16 header_offset, index_row;
87 header_offset = hb->show_headers ? hpp_list->nr_header_lines : 0;
88 browser->rows = browser->height - header_offset;
90 * Verify if we were at the last line and that line isn't
91 * visibe because we now show the header line(s).
93 index_row = browser->index - browser->top_idx;
94 if (index_row >= browser->rows)
95 browser->index -= index_row - browser->rows + 1;
98 static void hist_browser__refresh_dimensions(struct ui_browser *browser)
100 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
102 /* 3 == +/- toggle symbol before actual hist_entry rendering */
103 browser->width = 3 + (hists__sort_list_width(hb->hists) + sizeof("[k]"));
105 * FIXME: Just keeping existing behaviour, but this really should be
106 * before updating browser->width, as it will invalidate the
107 * calculation above. Fix this and the fallout in another
108 * changeset.
110 ui_browser__refresh_dimensions(browser);
111 hist_browser__update_rows(hb);
114 static void hist_browser__gotorc(struct hist_browser *browser, int row, int column)
116 struct hists *hists = browser->hists;
117 struct perf_hpp_list *hpp_list = hists->hpp_list;
118 u16 header_offset;
120 header_offset = browser->show_headers ? hpp_list->nr_header_lines : 0;
121 ui_browser__gotorc(&browser->b, row + header_offset, column);
124 static void hist_browser__reset(struct hist_browser *browser)
127 * The hists__remove_entry_filter() already folds non-filtered
128 * entries so we can assume it has 0 callchain rows.
130 browser->nr_callchain_rows = 0;
132 hist_browser__update_nr_entries(browser);
133 browser->b.nr_entries = hist_browser__nr_entries(browser);
134 hist_browser__refresh_dimensions(&browser->b);
135 ui_browser__reset_index(&browser->b);
138 static char tree__folded_sign(bool unfolded)
140 return unfolded ? '-' : '+';
143 static char hist_entry__folded(const struct hist_entry *he)
145 return he->has_children ? tree__folded_sign(he->unfolded) : ' ';
148 static char callchain_list__folded(const struct callchain_list *cl)
150 return cl->has_children ? tree__folded_sign(cl->unfolded) : ' ';
153 static void callchain_list__set_folding(struct callchain_list *cl, bool unfold)
155 cl->unfolded = unfold ? cl->has_children : false;
158 static struct inline_node *inline_node__create(struct map *map, u64 ip)
160 struct dso *dso;
161 struct inline_node *node;
163 if (map == NULL)
164 return NULL;
166 dso = map->dso;
167 if (dso == NULL)
168 return NULL;
170 node = dso__parse_addr_inlines(dso,
171 map__rip_2objdump(map, ip));
173 return node;
176 static int inline__count_rows(struct inline_node *node)
178 struct inline_list *ilist;
179 int i = 0;
181 if (node == NULL)
182 return 0;
184 list_for_each_entry(ilist, &node->val, list) {
185 if ((ilist->filename != NULL) || (ilist->funcname != NULL))
186 i++;
189 return i;
192 static int callchain_list__inline_rows(struct callchain_list *chain)
194 struct inline_node *node;
195 int rows;
197 node = inline_node__create(chain->ms.map, chain->ip);
198 if (node == NULL)
199 return 0;
201 rows = inline__count_rows(node);
202 inline_node__delete(node);
203 return rows;
206 static int callchain_node__count_rows_rb_tree(struct callchain_node *node)
208 int n = 0, inline_rows;
209 struct rb_node *nd;
211 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
212 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
213 struct callchain_list *chain;
214 char folded_sign = ' '; /* No children */
216 list_for_each_entry(chain, &child->val, list) {
217 ++n;
219 if (symbol_conf.inline_name) {
220 inline_rows =
221 callchain_list__inline_rows(chain);
222 n += inline_rows;
225 /* We need this because we may not have children */
226 folded_sign = callchain_list__folded(chain);
227 if (folded_sign == '+')
228 break;
231 if (folded_sign == '-') /* Have children and they're unfolded */
232 n += callchain_node__count_rows_rb_tree(child);
235 return n;
238 static int callchain_node__count_flat_rows(struct callchain_node *node)
240 struct callchain_list *chain;
241 char folded_sign = 0;
242 int n = 0;
244 list_for_each_entry(chain, &node->parent_val, list) {
245 if (!folded_sign) {
246 /* only check first chain list entry */
247 folded_sign = callchain_list__folded(chain);
248 if (folded_sign == '+')
249 return 1;
251 n++;
254 list_for_each_entry(chain, &node->val, list) {
255 if (!folded_sign) {
256 /* node->parent_val list might be empty */
257 folded_sign = callchain_list__folded(chain);
258 if (folded_sign == '+')
259 return 1;
261 n++;
264 return n;
267 static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused)
269 return 1;
272 static int callchain_node__count_rows(struct callchain_node *node)
274 struct callchain_list *chain;
275 bool unfolded = false;
276 int n = 0, inline_rows;
278 if (callchain_param.mode == CHAIN_FLAT)
279 return callchain_node__count_flat_rows(node);
280 else if (callchain_param.mode == CHAIN_FOLDED)
281 return callchain_node__count_folded_rows(node);
283 list_for_each_entry(chain, &node->val, list) {
284 ++n;
285 if (symbol_conf.inline_name) {
286 inline_rows = callchain_list__inline_rows(chain);
287 n += inline_rows;
290 unfolded = chain->unfolded;
293 if (unfolded)
294 n += callchain_node__count_rows_rb_tree(node);
296 return n;
299 static int callchain__count_rows(struct rb_root *chain)
301 struct rb_node *nd;
302 int n = 0;
304 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
305 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
306 n += callchain_node__count_rows(node);
309 return n;
312 static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he,
313 bool include_children)
315 int count = 0;
316 struct rb_node *node;
317 struct hist_entry *child;
319 if (he->leaf)
320 return callchain__count_rows(&he->sorted_chain);
322 if (he->has_no_entry)
323 return 1;
325 node = rb_first(&he->hroot_out);
326 while (node) {
327 float percent;
329 child = rb_entry(node, struct hist_entry, rb_node);
330 percent = hist_entry__get_percent_limit(child);
332 if (!child->filtered && percent >= hb->min_pcnt) {
333 count++;
335 if (include_children && child->unfolded)
336 count += hierarchy_count_rows(hb, child, true);
339 node = rb_next(node);
341 return count;
344 static bool hist_entry__toggle_fold(struct hist_entry *he)
346 if (!he)
347 return false;
349 if (!he->has_children)
350 return false;
352 he->unfolded = !he->unfolded;
353 return true;
356 static bool callchain_list__toggle_fold(struct callchain_list *cl)
358 if (!cl)
359 return false;
361 if (!cl->has_children)
362 return false;
364 cl->unfolded = !cl->unfolded;
365 return true;
368 static void callchain_node__init_have_children_rb_tree(struct callchain_node *node)
370 struct rb_node *nd = rb_first(&node->rb_root);
372 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
373 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
374 struct callchain_list *chain;
375 bool first = true;
377 list_for_each_entry(chain, &child->val, list) {
378 if (first) {
379 first = false;
380 chain->has_children = chain->list.next != &child->val ||
381 !RB_EMPTY_ROOT(&child->rb_root);
382 } else
383 chain->has_children = chain->list.next == &child->val &&
384 !RB_EMPTY_ROOT(&child->rb_root);
387 callchain_node__init_have_children_rb_tree(child);
391 static void callchain_node__init_have_children(struct callchain_node *node,
392 bool has_sibling)
394 struct callchain_list *chain;
396 chain = list_entry(node->val.next, struct callchain_list, list);
397 chain->has_children = has_sibling;
399 if (!list_empty(&node->val)) {
400 chain = list_entry(node->val.prev, struct callchain_list, list);
401 chain->has_children = !RB_EMPTY_ROOT(&node->rb_root);
404 callchain_node__init_have_children_rb_tree(node);
407 static void callchain__init_have_children(struct rb_root *root)
409 struct rb_node *nd = rb_first(root);
410 bool has_sibling = nd && rb_next(nd);
412 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
413 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
414 callchain_node__init_have_children(node, has_sibling);
415 if (callchain_param.mode == CHAIN_FLAT ||
416 callchain_param.mode == CHAIN_FOLDED)
417 callchain_node__make_parent_list(node);
421 static void hist_entry__init_have_children(struct hist_entry *he)
423 if (he->init_have_children)
424 return;
426 if (he->leaf) {
427 he->has_children = !RB_EMPTY_ROOT(&he->sorted_chain);
428 callchain__init_have_children(&he->sorted_chain);
429 } else {
430 he->has_children = !RB_EMPTY_ROOT(&he->hroot_out);
433 he->init_have_children = true;
436 static void hist_entry_init_inline_node(struct hist_entry *he)
438 if (he->inline_node)
439 return;
441 he->inline_node = inline_node__create(he->ms.map, he->ip);
443 if (he->inline_node == NULL)
444 return;
446 he->has_children = true;
449 static bool hist_browser__toggle_fold(struct hist_browser *browser)
451 struct hist_entry *he = browser->he_selection;
452 struct map_symbol *ms = browser->selection;
453 struct callchain_list *cl = container_of(ms, struct callchain_list, ms);
454 bool has_children;
456 if (!he || !ms)
457 return false;
459 if (ms == &he->ms)
460 has_children = hist_entry__toggle_fold(he);
461 else
462 has_children = callchain_list__toggle_fold(cl);
464 if (has_children) {
465 int child_rows = 0;
467 hist_entry__init_have_children(he);
468 browser->b.nr_entries -= he->nr_rows;
470 if (he->leaf)
471 browser->nr_callchain_rows -= he->nr_rows;
472 else
473 browser->nr_hierarchy_entries -= he->nr_rows;
475 if (symbol_conf.report_hierarchy)
476 child_rows = hierarchy_count_rows(browser, he, true);
478 if (he->unfolded) {
479 if (he->leaf)
480 if (he->inline_node)
481 he->nr_rows = inline__count_rows(
482 he->inline_node);
483 else
484 he->nr_rows = callchain__count_rows(
485 &he->sorted_chain);
486 else
487 he->nr_rows = hierarchy_count_rows(browser, he, false);
489 /* account grand children */
490 if (symbol_conf.report_hierarchy)
491 browser->b.nr_entries += child_rows - he->nr_rows;
493 if (!he->leaf && he->nr_rows == 0) {
494 he->has_no_entry = true;
495 he->nr_rows = 1;
497 } else {
498 if (symbol_conf.report_hierarchy)
499 browser->b.nr_entries -= child_rows - he->nr_rows;
501 if (he->has_no_entry)
502 he->has_no_entry = false;
504 he->nr_rows = 0;
507 browser->b.nr_entries += he->nr_rows;
509 if (he->leaf)
510 browser->nr_callchain_rows += he->nr_rows;
511 else
512 browser->nr_hierarchy_entries += he->nr_rows;
514 return true;
517 /* If it doesn't have children, no toggling performed */
518 return false;
521 static int callchain_node__set_folding_rb_tree(struct callchain_node *node, bool unfold)
523 int n = 0;
524 struct rb_node *nd;
526 for (nd = rb_first(&node->rb_root); nd; nd = rb_next(nd)) {
527 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
528 struct callchain_list *chain;
529 bool has_children = false;
531 list_for_each_entry(chain, &child->val, list) {
532 ++n;
533 callchain_list__set_folding(chain, unfold);
534 has_children = chain->has_children;
537 if (has_children)
538 n += callchain_node__set_folding_rb_tree(child, unfold);
541 return n;
544 static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
546 struct callchain_list *chain;
547 bool has_children = false;
548 int n = 0;
550 list_for_each_entry(chain, &node->val, list) {
551 ++n;
552 callchain_list__set_folding(chain, unfold);
553 has_children = chain->has_children;
556 if (has_children)
557 n += callchain_node__set_folding_rb_tree(node, unfold);
559 return n;
562 static int callchain__set_folding(struct rb_root *chain, bool unfold)
564 struct rb_node *nd;
565 int n = 0;
567 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
568 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
569 n += callchain_node__set_folding(node, unfold);
572 return n;
575 static int hierarchy_set_folding(struct hist_browser *hb, struct hist_entry *he,
576 bool unfold __maybe_unused)
578 float percent;
579 struct rb_node *nd;
580 struct hist_entry *child;
581 int n = 0;
583 for (nd = rb_first(&he->hroot_out); nd; nd = rb_next(nd)) {
584 child = rb_entry(nd, struct hist_entry, rb_node);
585 percent = hist_entry__get_percent_limit(child);
586 if (!child->filtered && percent >= hb->min_pcnt)
587 n++;
590 return n;
593 static void __hist_entry__set_folding(struct hist_entry *he,
594 struct hist_browser *hb, bool unfold)
596 hist_entry__init_have_children(he);
597 he->unfolded = unfold ? he->has_children : false;
599 if (he->has_children) {
600 int n;
602 if (he->leaf)
603 n = callchain__set_folding(&he->sorted_chain, unfold);
604 else
605 n = hierarchy_set_folding(hb, he, unfold);
607 he->nr_rows = unfold ? n : 0;
608 } else
609 he->nr_rows = 0;
612 static void hist_entry__set_folding(struct hist_entry *he,
613 struct hist_browser *browser, bool unfold)
615 double percent;
617 percent = hist_entry__get_percent_limit(he);
618 if (he->filtered || percent < browser->min_pcnt)
619 return;
621 __hist_entry__set_folding(he, browser, unfold);
623 if (!he->depth || unfold)
624 browser->nr_hierarchy_entries++;
625 if (he->leaf)
626 browser->nr_callchain_rows += he->nr_rows;
627 else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) {
628 browser->nr_hierarchy_entries++;
629 he->has_no_entry = true;
630 he->nr_rows = 1;
631 } else
632 he->has_no_entry = false;
635 static void
636 __hist_browser__set_folding(struct hist_browser *browser, bool unfold)
638 struct rb_node *nd;
639 struct hist_entry *he;
641 nd = rb_first(&browser->hists->entries);
642 while (nd) {
643 he = rb_entry(nd, struct hist_entry, rb_node);
645 /* set folding state even if it's currently folded */
646 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
648 hist_entry__set_folding(he, browser, unfold);
652 static void hist_browser__set_folding(struct hist_browser *browser, bool unfold)
654 browser->nr_hierarchy_entries = 0;
655 browser->nr_callchain_rows = 0;
656 __hist_browser__set_folding(browser, unfold);
658 browser->b.nr_entries = hist_browser__nr_entries(browser);
659 /* Go to the start, we may be way after valid entries after a collapse */
660 ui_browser__reset_index(&browser->b);
663 static void hist_browser__set_folding_selected(struct hist_browser *browser, bool unfold)
665 if (!browser->he_selection)
666 return;
668 hist_entry__set_folding(browser->he_selection, browser, unfold);
669 browser->b.nr_entries = hist_browser__nr_entries(browser);
672 static void ui_browser__warn_lost_events(struct ui_browser *browser)
674 ui_browser__warning(browser, 4,
675 "Events are being lost, check IO/CPU overload!\n\n"
676 "You may want to run 'perf' using a RT scheduler policy:\n\n"
677 " perf top -r 80\n\n"
678 "Or reduce the sampling frequency.");
681 static int hist_browser__title(struct hist_browser *browser, char *bf, size_t size)
683 return browser->title ? browser->title(browser, bf, size) : 0;
686 int hist_browser__run(struct hist_browser *browser, const char *help)
688 int key;
689 char title[160];
690 struct hist_browser_timer *hbt = browser->hbt;
691 int delay_secs = hbt ? hbt->refresh : 0;
693 browser->b.entries = &browser->hists->entries;
694 browser->b.nr_entries = hist_browser__nr_entries(browser);
696 hist_browser__title(browser, title, sizeof(title));
698 if (ui_browser__show(&browser->b, title, "%s", help) < 0)
699 return -1;
701 while (1) {
702 key = ui_browser__run(&browser->b, delay_secs);
704 switch (key) {
705 case K_TIMER: {
706 u64 nr_entries;
707 hbt->timer(hbt->arg);
709 if (hist_browser__has_filter(browser) ||
710 symbol_conf.report_hierarchy)
711 hist_browser__update_nr_entries(browser);
713 nr_entries = hist_browser__nr_entries(browser);
714 ui_browser__update_nr_entries(&browser->b, nr_entries);
716 if (browser->hists->stats.nr_lost_warned !=
717 browser->hists->stats.nr_events[PERF_RECORD_LOST]) {
718 browser->hists->stats.nr_lost_warned =
719 browser->hists->stats.nr_events[PERF_RECORD_LOST];
720 ui_browser__warn_lost_events(&browser->b);
723 hist_browser__title(browser, title, sizeof(title));
724 ui_browser__show_title(&browser->b, title);
725 continue;
727 case 'D': { /* Debug */
728 static int seq;
729 struct hist_entry *h = rb_entry(browser->b.top,
730 struct hist_entry, rb_node);
731 ui_helpline__pop();
732 ui_helpline__fpush("%d: nr_ent=(%d,%d), rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
733 seq++, browser->b.nr_entries,
734 browser->hists->nr_entries,
735 browser->b.rows,
736 browser->b.index,
737 browser->b.top_idx,
738 h->row_offset, h->nr_rows);
740 break;
741 case 'C':
742 /* Collapse the whole world. */
743 hist_browser__set_folding(browser, false);
744 break;
745 case 'c':
746 /* Collapse the selected entry. */
747 hist_browser__set_folding_selected(browser, false);
748 break;
749 case 'E':
750 /* Expand the whole world. */
751 hist_browser__set_folding(browser, true);
752 break;
753 case 'e':
754 /* Expand the selected entry. */
755 hist_browser__set_folding_selected(browser, true);
756 break;
757 case 'H':
758 browser->show_headers = !browser->show_headers;
759 hist_browser__update_rows(browser);
760 break;
761 case K_ENTER:
762 if (hist_browser__toggle_fold(browser))
763 break;
764 /* fall thru */
765 default:
766 goto out;
769 out:
770 ui_browser__hide(&browser->b);
771 return key;
774 struct callchain_print_arg {
775 /* for hists browser */
776 off_t row_offset;
777 bool is_current_entry;
779 /* for file dump */
780 FILE *fp;
781 int printed;
784 typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
785 struct callchain_list *chain,
786 const char *str, int offset,
787 unsigned short row,
788 struct callchain_print_arg *arg);
790 static void hist_browser__show_callchain_entry(struct hist_browser *browser,
791 struct callchain_list *chain,
792 const char *str, int offset,
793 unsigned short row,
794 struct callchain_print_arg *arg)
796 int color, width;
797 char folded_sign = callchain_list__folded(chain);
798 bool show_annotated = browser->show_dso && chain->ms.sym && symbol__annotation(chain->ms.sym)->src;
800 color = HE_COLORSET_NORMAL;
801 width = browser->b.width - (offset + 2);
802 if (ui_browser__is_current_entry(&browser->b, row)) {
803 browser->selection = &chain->ms;
804 color = HE_COLORSET_SELECTED;
805 arg->is_current_entry = true;
808 ui_browser__set_color(&browser->b, color);
809 hist_browser__gotorc(browser, row, 0);
810 ui_browser__write_nstring(&browser->b, " ", offset);
811 ui_browser__printf(&browser->b, "%c", folded_sign);
812 ui_browser__write_graph(&browser->b, show_annotated ? SLSMG_RARROW_CHAR : ' ');
813 ui_browser__write_nstring(&browser->b, str, width);
816 static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
817 struct callchain_list *chain,
818 const char *str, int offset,
819 unsigned short row __maybe_unused,
820 struct callchain_print_arg *arg)
822 char folded_sign = callchain_list__folded(chain);
824 arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
825 folded_sign, str);
828 typedef bool (*check_output_full_fn)(struct hist_browser *browser,
829 unsigned short row);
831 static bool hist_browser__check_output_full(struct hist_browser *browser,
832 unsigned short row)
834 return browser->b.rows == row;
837 static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
838 unsigned short row __maybe_unused)
840 return false;
843 #define LEVEL_OFFSET_STEP 3
845 static int hist_browser__show_inline(struct hist_browser *browser,
846 struct inline_node *node,
847 unsigned short row,
848 int offset)
850 struct inline_list *ilist;
851 char buf[1024];
852 int color, width, first_row;
854 first_row = row;
855 width = browser->b.width - (LEVEL_OFFSET_STEP + 2);
856 list_for_each_entry(ilist, &node->val, list) {
857 if ((ilist->filename != NULL) || (ilist->funcname != NULL)) {
858 color = HE_COLORSET_NORMAL;
859 if (ui_browser__is_current_entry(&browser->b, row))
860 color = HE_COLORSET_SELECTED;
862 if (callchain_param.key == CCKEY_ADDRESS ||
863 callchain_param.key == CCKEY_SRCLINE) {
864 if (ilist->filename != NULL)
865 scnprintf(buf, sizeof(buf),
866 "%s:%d (inline)",
867 ilist->filename,
868 ilist->line_nr);
869 else
870 scnprintf(buf, sizeof(buf), "??");
871 } else if (ilist->funcname != NULL)
872 scnprintf(buf, sizeof(buf), "%s (inline)",
873 ilist->funcname);
874 else if (ilist->filename != NULL)
875 scnprintf(buf, sizeof(buf),
876 "%s:%d (inline)",
877 ilist->filename,
878 ilist->line_nr);
879 else
880 scnprintf(buf, sizeof(buf), "??");
882 ui_browser__set_color(&browser->b, color);
883 hist_browser__gotorc(browser, row, 0);
884 ui_browser__write_nstring(&browser->b, " ",
885 LEVEL_OFFSET_STEP + offset);
886 ui_browser__write_nstring(&browser->b, buf, width);
887 row++;
891 return row - first_row;
894 static size_t show_inline_list(struct hist_browser *browser, struct map *map,
895 u64 ip, int row, int offset)
897 struct inline_node *node;
898 int ret;
900 node = inline_node__create(map, ip);
901 if (node == NULL)
902 return 0;
904 ret = hist_browser__show_inline(browser, node, row, offset);
906 inline_node__delete(node);
907 return ret;
910 static int hist_browser__show_callchain_list(struct hist_browser *browser,
911 struct callchain_node *node,
912 struct callchain_list *chain,
913 unsigned short row, u64 total,
914 bool need_percent, int offset,
915 print_callchain_entry_fn print,
916 struct callchain_print_arg *arg)
918 char bf[1024], *alloc_str;
919 char buf[64], *alloc_str2;
920 const char *str;
921 int inline_rows = 0, ret = 1;
923 if (arg->row_offset != 0) {
924 arg->row_offset--;
925 return 0;
928 alloc_str = NULL;
929 alloc_str2 = NULL;
931 str = callchain_list__sym_name(chain, bf, sizeof(bf),
932 browser->show_dso);
934 if (symbol_conf.show_branchflag_count) {
935 callchain_list_counts__printf_value(chain, NULL,
936 buf, sizeof(buf));
938 if (asprintf(&alloc_str2, "%s%s", str, buf) < 0)
939 str = "Not enough memory!";
940 else
941 str = alloc_str2;
944 if (need_percent) {
945 callchain_node__scnprintf_value(node, buf, sizeof(buf),
946 total);
948 if (asprintf(&alloc_str, "%s %s", buf, str) < 0)
949 str = "Not enough memory!";
950 else
951 str = alloc_str;
954 print(browser, chain, str, offset, row, arg);
955 free(alloc_str);
956 free(alloc_str2);
958 if (symbol_conf.inline_name) {
959 inline_rows = show_inline_list(browser, chain->ms.map,
960 chain->ip, row + 1, offset);
963 return ret + inline_rows;
966 static bool check_percent_display(struct rb_node *node, u64 parent_total)
968 struct callchain_node *child;
970 if (node == NULL)
971 return false;
973 if (rb_next(node))
974 return true;
976 child = rb_entry(node, struct callchain_node, rb_node);
977 return callchain_cumul_hits(child) != parent_total;
980 static int hist_browser__show_callchain_flat(struct hist_browser *browser,
981 struct rb_root *root,
982 unsigned short row, u64 total,
983 u64 parent_total,
984 print_callchain_entry_fn print,
985 struct callchain_print_arg *arg,
986 check_output_full_fn is_output_full)
988 struct rb_node *node;
989 int first_row = row, offset = LEVEL_OFFSET_STEP;
990 bool need_percent;
992 node = rb_first(root);
993 need_percent = check_percent_display(node, parent_total);
995 while (node) {
996 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
997 struct rb_node *next = rb_next(node);
998 struct callchain_list *chain;
999 char folded_sign = ' ';
1000 int first = true;
1001 int extra_offset = 0;
1003 list_for_each_entry(chain, &child->parent_val, list) {
1004 bool was_first = first;
1006 if (first)
1007 first = false;
1008 else if (need_percent)
1009 extra_offset = LEVEL_OFFSET_STEP;
1011 folded_sign = callchain_list__folded(chain);
1013 row += hist_browser__show_callchain_list(browser, child,
1014 chain, row, total,
1015 was_first && need_percent,
1016 offset + extra_offset,
1017 print, arg);
1019 if (is_output_full(browser, row))
1020 goto out;
1022 if (folded_sign == '+')
1023 goto next;
1026 list_for_each_entry(chain, &child->val, list) {
1027 bool was_first = first;
1029 if (first)
1030 first = false;
1031 else if (need_percent)
1032 extra_offset = LEVEL_OFFSET_STEP;
1034 folded_sign = callchain_list__folded(chain);
1036 row += hist_browser__show_callchain_list(browser, child,
1037 chain, row, total,
1038 was_first && need_percent,
1039 offset + extra_offset,
1040 print, arg);
1042 if (is_output_full(browser, row))
1043 goto out;
1045 if (folded_sign == '+')
1046 break;
1049 next:
1050 if (is_output_full(browser, row))
1051 break;
1052 node = next;
1054 out:
1055 return row - first_row;
1058 static char *hist_browser__folded_callchain_str(struct hist_browser *browser,
1059 struct callchain_list *chain,
1060 char *value_str, char *old_str)
1062 char bf[1024];
1063 const char *str;
1064 char *new;
1066 str = callchain_list__sym_name(chain, bf, sizeof(bf),
1067 browser->show_dso);
1068 if (old_str) {
1069 if (asprintf(&new, "%s%s%s", old_str,
1070 symbol_conf.field_sep ?: ";", str) < 0)
1071 new = NULL;
1072 } else {
1073 if (value_str) {
1074 if (asprintf(&new, "%s %s", value_str, str) < 0)
1075 new = NULL;
1076 } else {
1077 if (asprintf(&new, "%s", str) < 0)
1078 new = NULL;
1081 return new;
1084 static int hist_browser__show_callchain_folded(struct hist_browser *browser,
1085 struct rb_root *root,
1086 unsigned short row, u64 total,
1087 u64 parent_total,
1088 print_callchain_entry_fn print,
1089 struct callchain_print_arg *arg,
1090 check_output_full_fn is_output_full)
1092 struct rb_node *node;
1093 int first_row = row, offset = LEVEL_OFFSET_STEP;
1094 bool need_percent;
1096 node = rb_first(root);
1097 need_percent = check_percent_display(node, parent_total);
1099 while (node) {
1100 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1101 struct rb_node *next = rb_next(node);
1102 struct callchain_list *chain, *first_chain = NULL;
1103 int first = true;
1104 char *value_str = NULL, *value_str_alloc = NULL;
1105 char *chain_str = NULL, *chain_str_alloc = NULL;
1107 if (arg->row_offset != 0) {
1108 arg->row_offset--;
1109 goto next;
1112 if (need_percent) {
1113 char buf[64];
1115 callchain_node__scnprintf_value(child, buf, sizeof(buf), total);
1116 if (asprintf(&value_str, "%s", buf) < 0) {
1117 value_str = (char *)"<...>";
1118 goto do_print;
1120 value_str_alloc = value_str;
1123 list_for_each_entry(chain, &child->parent_val, list) {
1124 chain_str = hist_browser__folded_callchain_str(browser,
1125 chain, value_str, chain_str);
1126 if (first) {
1127 first = false;
1128 first_chain = chain;
1131 if (chain_str == NULL) {
1132 chain_str = (char *)"Not enough memory!";
1133 goto do_print;
1136 chain_str_alloc = chain_str;
1139 list_for_each_entry(chain, &child->val, list) {
1140 chain_str = hist_browser__folded_callchain_str(browser,
1141 chain, value_str, chain_str);
1142 if (first) {
1143 first = false;
1144 first_chain = chain;
1147 if (chain_str == NULL) {
1148 chain_str = (char *)"Not enough memory!";
1149 goto do_print;
1152 chain_str_alloc = chain_str;
1155 do_print:
1156 print(browser, first_chain, chain_str, offset, row++, arg);
1157 free(value_str_alloc);
1158 free(chain_str_alloc);
1160 next:
1161 if (is_output_full(browser, row))
1162 break;
1163 node = next;
1166 return row - first_row;
1169 static int hist_browser__show_callchain_graph(struct hist_browser *browser,
1170 struct rb_root *root, int level,
1171 unsigned short row, u64 total,
1172 u64 parent_total,
1173 print_callchain_entry_fn print,
1174 struct callchain_print_arg *arg,
1175 check_output_full_fn is_output_full)
1177 struct rb_node *node;
1178 int first_row = row, offset = level * LEVEL_OFFSET_STEP;
1179 bool need_percent;
1180 u64 percent_total = total;
1182 if (callchain_param.mode == CHAIN_GRAPH_REL)
1183 percent_total = parent_total;
1185 node = rb_first(root);
1186 need_percent = check_percent_display(node, parent_total);
1188 while (node) {
1189 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
1190 struct rb_node *next = rb_next(node);
1191 struct callchain_list *chain;
1192 char folded_sign = ' ';
1193 int first = true;
1194 int extra_offset = 0;
1196 list_for_each_entry(chain, &child->val, list) {
1197 bool was_first = first;
1199 if (first)
1200 first = false;
1201 else if (need_percent)
1202 extra_offset = LEVEL_OFFSET_STEP;
1204 folded_sign = callchain_list__folded(chain);
1206 row += hist_browser__show_callchain_list(browser, child,
1207 chain, row, percent_total,
1208 was_first && need_percent,
1209 offset + extra_offset,
1210 print, arg);
1212 if (is_output_full(browser, row))
1213 goto out;
1215 if (folded_sign == '+')
1216 break;
1219 if (folded_sign == '-') {
1220 const int new_level = level + (extra_offset ? 2 : 1);
1222 row += hist_browser__show_callchain_graph(browser, &child->rb_root,
1223 new_level, row, total,
1224 child->children_hit,
1225 print, arg, is_output_full);
1227 if (is_output_full(browser, row))
1228 break;
1229 node = next;
1231 out:
1232 return row - first_row;
1235 static int hist_browser__show_callchain(struct hist_browser *browser,
1236 struct hist_entry *entry, int level,
1237 unsigned short row,
1238 print_callchain_entry_fn print,
1239 struct callchain_print_arg *arg,
1240 check_output_full_fn is_output_full)
1242 u64 total = hists__total_period(entry->hists);
1243 u64 parent_total;
1244 int printed;
1246 if (symbol_conf.cumulate_callchain)
1247 parent_total = entry->stat_acc->period;
1248 else
1249 parent_total = entry->stat.period;
1251 if (callchain_param.mode == CHAIN_FLAT) {
1252 printed = hist_browser__show_callchain_flat(browser,
1253 &entry->sorted_chain, row,
1254 total, parent_total, print, arg,
1255 is_output_full);
1256 } else if (callchain_param.mode == CHAIN_FOLDED) {
1257 printed = hist_browser__show_callchain_folded(browser,
1258 &entry->sorted_chain, row,
1259 total, parent_total, print, arg,
1260 is_output_full);
1261 } else {
1262 printed = hist_browser__show_callchain_graph(browser,
1263 &entry->sorted_chain, level, row,
1264 total, parent_total, print, arg,
1265 is_output_full);
1268 if (arg->is_current_entry)
1269 browser->he_selection = entry;
1271 return printed;
1274 struct hpp_arg {
1275 struct ui_browser *b;
1276 char folded_sign;
1277 bool current_entry;
1280 int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...)
1282 struct hpp_arg *arg = hpp->ptr;
1283 int ret, len;
1284 va_list args;
1285 double percent;
1287 va_start(args, fmt);
1288 len = va_arg(args, int);
1289 percent = va_arg(args, double);
1290 va_end(args);
1292 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
1294 ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent);
1295 ui_browser__printf(arg->b, "%s", hpp->buf);
1297 return ret;
1300 #define __HPP_COLOR_PERCENT_FN(_type, _field) \
1301 static u64 __hpp_get_##_field(struct hist_entry *he) \
1303 return he->stat._field; \
1306 static int \
1307 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1308 struct perf_hpp *hpp, \
1309 struct hist_entry *he) \
1311 return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \
1312 __hpp__slsmg_color_printf, true); \
1315 #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \
1316 static u64 __hpp_get_acc_##_field(struct hist_entry *he) \
1318 return he->stat_acc->_field; \
1321 static int \
1322 hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \
1323 struct perf_hpp *hpp, \
1324 struct hist_entry *he) \
1326 if (!symbol_conf.cumulate_callchain) { \
1327 struct hpp_arg *arg = hpp->ptr; \
1328 int len = fmt->user_len ?: fmt->len; \
1329 int ret = scnprintf(hpp->buf, hpp->size, \
1330 "%*s", len, "N/A"); \
1331 ui_browser__printf(arg->b, "%s", hpp->buf); \
1333 return ret; \
1335 return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \
1336 " %*.2f%%", __hpp__slsmg_color_printf, true); \
1339 __HPP_COLOR_PERCENT_FN(overhead, period)
1340 __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
1341 __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
1342 __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
1343 __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
1344 __HPP_COLOR_ACC_PERCENT_FN(overhead_acc, period)
1346 #undef __HPP_COLOR_PERCENT_FN
1347 #undef __HPP_COLOR_ACC_PERCENT_FN
1349 void hist_browser__init_hpp(void)
1351 perf_hpp__format[PERF_HPP__OVERHEAD].color =
1352 hist_browser__hpp_color_overhead;
1353 perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
1354 hist_browser__hpp_color_overhead_sys;
1355 perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
1356 hist_browser__hpp_color_overhead_us;
1357 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
1358 hist_browser__hpp_color_overhead_guest_sys;
1359 perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
1360 hist_browser__hpp_color_overhead_guest_us;
1361 perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
1362 hist_browser__hpp_color_overhead_acc;
1365 static int hist_browser__show_entry(struct hist_browser *browser,
1366 struct hist_entry *entry,
1367 unsigned short row)
1369 int printed = 0;
1370 int width = browser->b.width;
1371 char folded_sign = ' ';
1372 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1373 off_t row_offset = entry->row_offset;
1374 bool first = true;
1375 struct perf_hpp_fmt *fmt;
1377 if (current_entry) {
1378 browser->he_selection = entry;
1379 browser->selection = &entry->ms;
1382 if (symbol_conf.use_callchain) {
1383 hist_entry__init_have_children(entry);
1384 folded_sign = hist_entry__folded(entry);
1387 if (symbol_conf.inline_name &&
1388 (!entry->has_children)) {
1389 hist_entry_init_inline_node(entry);
1390 folded_sign = hist_entry__folded(entry);
1393 if (row_offset == 0) {
1394 struct hpp_arg arg = {
1395 .b = &browser->b,
1396 .folded_sign = folded_sign,
1397 .current_entry = current_entry,
1399 int column = 0;
1401 hist_browser__gotorc(browser, row, 0);
1403 hists__for_each_format(browser->hists, fmt) {
1404 char s[2048];
1405 struct perf_hpp hpp = {
1406 .buf = s,
1407 .size = sizeof(s),
1408 .ptr = &arg,
1411 if (perf_hpp__should_skip(fmt, entry->hists) ||
1412 column++ < browser->b.horiz_scroll)
1413 continue;
1415 if (current_entry && browser->b.navkeypressed) {
1416 ui_browser__set_color(&browser->b,
1417 HE_COLORSET_SELECTED);
1418 } else {
1419 ui_browser__set_color(&browser->b,
1420 HE_COLORSET_NORMAL);
1423 if (first) {
1424 if (symbol_conf.use_callchain ||
1425 symbol_conf.inline_name) {
1426 ui_browser__printf(&browser->b, "%c ", folded_sign);
1427 width -= 2;
1429 first = false;
1430 } else {
1431 ui_browser__printf(&browser->b, " ");
1432 width -= 2;
1435 if (fmt->color) {
1436 int ret = fmt->color(fmt, &hpp, entry);
1437 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1439 * fmt->color() already used ui_browser to
1440 * print the non alignment bits, skip it (+ret):
1442 ui_browser__printf(&browser->b, "%s", s + ret);
1443 } else {
1444 hist_entry__snprintf_alignment(entry, &hpp, fmt, fmt->entry(fmt, &hpp, entry));
1445 ui_browser__printf(&browser->b, "%s", s);
1447 width -= hpp.buf - s;
1450 /* The scroll bar isn't being used */
1451 if (!browser->b.navkeypressed)
1452 width += 1;
1454 ui_browser__write_nstring(&browser->b, "", width);
1456 ++row;
1457 ++printed;
1458 } else
1459 --row_offset;
1461 if (folded_sign == '-' && row != browser->b.rows) {
1462 struct callchain_print_arg arg = {
1463 .row_offset = row_offset,
1464 .is_current_entry = current_entry,
1467 if (entry->inline_node)
1468 printed += hist_browser__show_inline(browser,
1469 entry->inline_node, row, 0);
1470 else
1471 printed += hist_browser__show_callchain(browser,
1472 entry, 1, row,
1473 hist_browser__show_callchain_entry,
1474 &arg,
1475 hist_browser__check_output_full);
1478 return printed;
1481 static int hist_browser__show_hierarchy_entry(struct hist_browser *browser,
1482 struct hist_entry *entry,
1483 unsigned short row,
1484 int level)
1486 int printed = 0;
1487 int width = browser->b.width;
1488 char folded_sign = ' ';
1489 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1490 off_t row_offset = entry->row_offset;
1491 bool first = true;
1492 struct perf_hpp_fmt *fmt;
1493 struct perf_hpp_list_node *fmt_node;
1494 struct hpp_arg arg = {
1495 .b = &browser->b,
1496 .current_entry = current_entry,
1498 int column = 0;
1499 int hierarchy_indent = (entry->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
1501 if (current_entry) {
1502 browser->he_selection = entry;
1503 browser->selection = &entry->ms;
1506 hist_entry__init_have_children(entry);
1507 folded_sign = hist_entry__folded(entry);
1508 arg.folded_sign = folded_sign;
1510 if (entry->leaf && row_offset) {
1511 row_offset--;
1512 goto show_callchain;
1515 hist_browser__gotorc(browser, row, 0);
1517 if (current_entry && browser->b.navkeypressed)
1518 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1519 else
1520 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1522 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1523 width -= level * HIERARCHY_INDENT;
1525 /* the first hpp_list_node is for overhead columns */
1526 fmt_node = list_first_entry(&entry->hists->hpp_formats,
1527 struct perf_hpp_list_node, list);
1528 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1529 char s[2048];
1530 struct perf_hpp hpp = {
1531 .buf = s,
1532 .size = sizeof(s),
1533 .ptr = &arg,
1536 if (perf_hpp__should_skip(fmt, entry->hists) ||
1537 column++ < browser->b.horiz_scroll)
1538 continue;
1540 if (current_entry && browser->b.navkeypressed) {
1541 ui_browser__set_color(&browser->b,
1542 HE_COLORSET_SELECTED);
1543 } else {
1544 ui_browser__set_color(&browser->b,
1545 HE_COLORSET_NORMAL);
1548 if (first) {
1549 ui_browser__printf(&browser->b, "%c ", folded_sign);
1550 width -= 2;
1551 first = false;
1552 } else {
1553 ui_browser__printf(&browser->b, " ");
1554 width -= 2;
1557 if (fmt->color) {
1558 int ret = fmt->color(fmt, &hpp, entry);
1559 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1561 * fmt->color() already used ui_browser to
1562 * print the non alignment bits, skip it (+ret):
1564 ui_browser__printf(&browser->b, "%s", s + ret);
1565 } else {
1566 int ret = fmt->entry(fmt, &hpp, entry);
1567 hist_entry__snprintf_alignment(entry, &hpp, fmt, ret);
1568 ui_browser__printf(&browser->b, "%s", s);
1570 width -= hpp.buf - s;
1573 if (!first) {
1574 ui_browser__write_nstring(&browser->b, "", hierarchy_indent);
1575 width -= hierarchy_indent;
1578 if (column >= browser->b.horiz_scroll) {
1579 char s[2048];
1580 struct perf_hpp hpp = {
1581 .buf = s,
1582 .size = sizeof(s),
1583 .ptr = &arg,
1586 if (current_entry && browser->b.navkeypressed) {
1587 ui_browser__set_color(&browser->b,
1588 HE_COLORSET_SELECTED);
1589 } else {
1590 ui_browser__set_color(&browser->b,
1591 HE_COLORSET_NORMAL);
1594 perf_hpp_list__for_each_format(entry->hpp_list, fmt) {
1595 if (first) {
1596 ui_browser__printf(&browser->b, "%c ", folded_sign);
1597 first = false;
1598 } else {
1599 ui_browser__write_nstring(&browser->b, "", 2);
1602 width -= 2;
1605 * No need to call hist_entry__snprintf_alignment()
1606 * since this fmt is always the last column in the
1607 * hierarchy mode.
1609 if (fmt->color) {
1610 width -= fmt->color(fmt, &hpp, entry);
1611 } else {
1612 int i = 0;
1614 width -= fmt->entry(fmt, &hpp, entry);
1615 ui_browser__printf(&browser->b, "%s", ltrim(s));
1617 while (isspace(s[i++]))
1618 width++;
1623 /* The scroll bar isn't being used */
1624 if (!browser->b.navkeypressed)
1625 width += 1;
1627 ui_browser__write_nstring(&browser->b, "", width);
1629 ++row;
1630 ++printed;
1632 show_callchain:
1633 if (entry->leaf && folded_sign == '-' && row != browser->b.rows) {
1634 struct callchain_print_arg carg = {
1635 .row_offset = row_offset,
1638 printed += hist_browser__show_callchain(browser, entry,
1639 level + 1, row,
1640 hist_browser__show_callchain_entry, &carg,
1641 hist_browser__check_output_full);
1644 return printed;
1647 static int hist_browser__show_no_entry(struct hist_browser *browser,
1648 unsigned short row, int level)
1650 int width = browser->b.width;
1651 bool current_entry = ui_browser__is_current_entry(&browser->b, row);
1652 bool first = true;
1653 int column = 0;
1654 int ret;
1655 struct perf_hpp_fmt *fmt;
1656 struct perf_hpp_list_node *fmt_node;
1657 int indent = browser->hists->nr_hpp_node - 2;
1659 if (current_entry) {
1660 browser->he_selection = NULL;
1661 browser->selection = NULL;
1664 hist_browser__gotorc(browser, row, 0);
1666 if (current_entry && browser->b.navkeypressed)
1667 ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED);
1668 else
1669 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
1671 ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT);
1672 width -= level * HIERARCHY_INDENT;
1674 /* the first hpp_list_node is for overhead columns */
1675 fmt_node = list_first_entry(&browser->hists->hpp_formats,
1676 struct perf_hpp_list_node, list);
1677 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1678 if (perf_hpp__should_skip(fmt, browser->hists) ||
1679 column++ < browser->b.horiz_scroll)
1680 continue;
1682 ret = fmt->width(fmt, NULL, browser->hists);
1684 if (first) {
1685 /* for folded sign */
1686 first = false;
1687 ret++;
1688 } else {
1689 /* space between columns */
1690 ret += 2;
1693 ui_browser__write_nstring(&browser->b, "", ret);
1694 width -= ret;
1697 ui_browser__write_nstring(&browser->b, "", indent * HIERARCHY_INDENT);
1698 width -= indent * HIERARCHY_INDENT;
1700 if (column >= browser->b.horiz_scroll) {
1701 char buf[32];
1703 ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt);
1704 ui_browser__printf(&browser->b, " %s", buf);
1705 width -= ret + 2;
1708 /* The scroll bar isn't being used */
1709 if (!browser->b.navkeypressed)
1710 width += 1;
1712 ui_browser__write_nstring(&browser->b, "", width);
1713 return 1;
1716 static int advance_hpp_check(struct perf_hpp *hpp, int inc)
1718 advance_hpp(hpp, inc);
1719 return hpp->size <= 0;
1722 static int
1723 hists_browser__scnprintf_headers(struct hist_browser *browser, char *buf,
1724 size_t size, int line)
1726 struct hists *hists = browser->hists;
1727 struct perf_hpp dummy_hpp = {
1728 .buf = buf,
1729 .size = size,
1731 struct perf_hpp_fmt *fmt;
1732 size_t ret = 0;
1733 int column = 0;
1734 int span = 0;
1736 if (symbol_conf.use_callchain) {
1737 ret = scnprintf(buf, size, " ");
1738 if (advance_hpp_check(&dummy_hpp, ret))
1739 return ret;
1742 hists__for_each_format(browser->hists, fmt) {
1743 if (perf_hpp__should_skip(fmt, hists) || column++ < browser->b.horiz_scroll)
1744 continue;
1746 ret = fmt->header(fmt, &dummy_hpp, hists, line, &span);
1747 if (advance_hpp_check(&dummy_hpp, ret))
1748 break;
1750 if (span)
1751 continue;
1753 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1754 if (advance_hpp_check(&dummy_hpp, ret))
1755 break;
1758 return ret;
1761 static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *browser, char *buf, size_t size)
1763 struct hists *hists = browser->hists;
1764 struct perf_hpp dummy_hpp = {
1765 .buf = buf,
1766 .size = size,
1768 struct perf_hpp_fmt *fmt;
1769 struct perf_hpp_list_node *fmt_node;
1770 size_t ret = 0;
1771 int column = 0;
1772 int indent = hists->nr_hpp_node - 2;
1773 bool first_node, first_col;
1775 ret = scnprintf(buf, size, " ");
1776 if (advance_hpp_check(&dummy_hpp, ret))
1777 return ret;
1779 first_node = true;
1780 /* the first hpp_list_node is for overhead columns */
1781 fmt_node = list_first_entry(&hists->hpp_formats,
1782 struct perf_hpp_list_node, list);
1783 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1784 if (column++ < browser->b.horiz_scroll)
1785 continue;
1787 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1788 if (advance_hpp_check(&dummy_hpp, ret))
1789 break;
1791 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " ");
1792 if (advance_hpp_check(&dummy_hpp, ret))
1793 break;
1795 first_node = false;
1798 if (!first_node) {
1799 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "%*s",
1800 indent * HIERARCHY_INDENT, "");
1801 if (advance_hpp_check(&dummy_hpp, ret))
1802 return ret;
1805 first_node = true;
1806 list_for_each_entry_continue(fmt_node, &hists->hpp_formats, list) {
1807 if (!first_node) {
1808 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, " / ");
1809 if (advance_hpp_check(&dummy_hpp, ret))
1810 break;
1812 first_node = false;
1814 first_col = true;
1815 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
1816 char *start;
1818 if (perf_hpp__should_skip(fmt, hists))
1819 continue;
1821 if (!first_col) {
1822 ret = scnprintf(dummy_hpp.buf, dummy_hpp.size, "+");
1823 if (advance_hpp_check(&dummy_hpp, ret))
1824 break;
1826 first_col = false;
1828 ret = fmt->header(fmt, &dummy_hpp, hists, 0, NULL);
1829 dummy_hpp.buf[ret] = '\0';
1831 start = trim(dummy_hpp.buf);
1832 ret = strlen(start);
1834 if (start != dummy_hpp.buf)
1835 memmove(dummy_hpp.buf, start, ret + 1);
1837 if (advance_hpp_check(&dummy_hpp, ret))
1838 break;
1842 return ret;
1845 static void hists_browser__hierarchy_headers(struct hist_browser *browser)
1847 char headers[1024];
1849 hists_browser__scnprintf_hierarchy_headers(browser, headers,
1850 sizeof(headers));
1852 ui_browser__gotorc(&browser->b, 0, 0);
1853 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1854 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1857 static void hists_browser__headers(struct hist_browser *browser)
1859 struct hists *hists = browser->hists;
1860 struct perf_hpp_list *hpp_list = hists->hpp_list;
1862 int line;
1864 for (line = 0; line < hpp_list->nr_header_lines; line++) {
1865 char headers[1024];
1867 hists_browser__scnprintf_headers(browser, headers,
1868 sizeof(headers), line);
1870 ui_browser__gotorc(&browser->b, line, 0);
1871 ui_browser__set_color(&browser->b, HE_COLORSET_ROOT);
1872 ui_browser__write_nstring(&browser->b, headers, browser->b.width + 1);
1876 static void hist_browser__show_headers(struct hist_browser *browser)
1878 if (symbol_conf.report_hierarchy)
1879 hists_browser__hierarchy_headers(browser);
1880 else
1881 hists_browser__headers(browser);
1884 static void ui_browser__hists_init_top(struct ui_browser *browser)
1886 if (browser->top == NULL) {
1887 struct hist_browser *hb;
1889 hb = container_of(browser, struct hist_browser, b);
1890 browser->top = rb_first(&hb->hists->entries);
1894 static unsigned int hist_browser__refresh(struct ui_browser *browser)
1896 unsigned row = 0;
1897 u16 header_offset = 0;
1898 struct rb_node *nd;
1899 struct hist_browser *hb = container_of(browser, struct hist_browser, b);
1900 struct hists *hists = hb->hists;
1902 if (hb->show_headers) {
1903 struct perf_hpp_list *hpp_list = hists->hpp_list;
1905 hist_browser__show_headers(hb);
1906 header_offset = hpp_list->nr_header_lines;
1909 ui_browser__hists_init_top(browser);
1910 hb->he_selection = NULL;
1911 hb->selection = NULL;
1913 for (nd = browser->top; nd; nd = rb_hierarchy_next(nd)) {
1914 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1915 float percent;
1917 if (h->filtered) {
1918 /* let it move to sibling */
1919 h->unfolded = false;
1920 continue;
1923 percent = hist_entry__get_percent_limit(h);
1924 if (percent < hb->min_pcnt)
1925 continue;
1927 if (symbol_conf.report_hierarchy) {
1928 row += hist_browser__show_hierarchy_entry(hb, h, row,
1929 h->depth);
1930 if (row == browser->rows)
1931 break;
1933 if (h->has_no_entry) {
1934 hist_browser__show_no_entry(hb, row, h->depth + 1);
1935 row++;
1937 } else {
1938 row += hist_browser__show_entry(hb, h, row);
1941 if (row == browser->rows)
1942 break;
1945 return row + header_offset;
1948 static struct rb_node *hists__filter_entries(struct rb_node *nd,
1949 float min_pcnt)
1951 while (nd != NULL) {
1952 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1953 float percent = hist_entry__get_percent_limit(h);
1955 if (!h->filtered && percent >= min_pcnt)
1956 return nd;
1959 * If it's filtered, its all children also were filtered.
1960 * So move to sibling node.
1962 if (rb_next(nd))
1963 nd = rb_next(nd);
1964 else
1965 nd = rb_hierarchy_next(nd);
1968 return NULL;
1971 static struct rb_node *hists__filter_prev_entries(struct rb_node *nd,
1972 float min_pcnt)
1974 while (nd != NULL) {
1975 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
1976 float percent = hist_entry__get_percent_limit(h);
1978 if (!h->filtered && percent >= min_pcnt)
1979 return nd;
1981 nd = rb_hierarchy_prev(nd);
1984 return NULL;
1987 static void ui_browser__hists_seek(struct ui_browser *browser,
1988 off_t offset, int whence)
1990 struct hist_entry *h;
1991 struct rb_node *nd;
1992 bool first = true;
1993 struct hist_browser *hb;
1995 hb = container_of(browser, struct hist_browser, b);
1997 if (browser->nr_entries == 0)
1998 return;
2000 ui_browser__hists_init_top(browser);
2002 switch (whence) {
2003 case SEEK_SET:
2004 nd = hists__filter_entries(rb_first(browser->entries),
2005 hb->min_pcnt);
2006 break;
2007 case SEEK_CUR:
2008 nd = browser->top;
2009 goto do_offset;
2010 case SEEK_END:
2011 nd = rb_hierarchy_last(rb_last(browser->entries));
2012 nd = hists__filter_prev_entries(nd, hb->min_pcnt);
2013 first = false;
2014 break;
2015 default:
2016 return;
2020 * Moves not relative to the first visible entry invalidates its
2021 * row_offset:
2023 h = rb_entry(browser->top, struct hist_entry, rb_node);
2024 h->row_offset = 0;
2027 * Here we have to check if nd is expanded (+), if it is we can't go
2028 * the next top level hist_entry, instead we must compute an offset of
2029 * what _not_ to show and not change the first visible entry.
2031 * This offset increments when we are going from top to bottom and
2032 * decreases when we're going from bottom to top.
2034 * As we don't have backpointers to the top level in the callchains
2035 * structure, we need to always print the whole hist_entry callchain,
2036 * skipping the first ones that are before the first visible entry
2037 * and stop when we printed enough lines to fill the screen.
2039 do_offset:
2040 if (!nd)
2041 return;
2043 if (offset > 0) {
2044 do {
2045 h = rb_entry(nd, struct hist_entry, rb_node);
2046 if (h->unfolded && h->leaf) {
2047 u16 remaining = h->nr_rows - h->row_offset;
2048 if (offset > remaining) {
2049 offset -= remaining;
2050 h->row_offset = 0;
2051 } else {
2052 h->row_offset += offset;
2053 offset = 0;
2054 browser->top = nd;
2055 break;
2058 nd = hists__filter_entries(rb_hierarchy_next(nd),
2059 hb->min_pcnt);
2060 if (nd == NULL)
2061 break;
2062 --offset;
2063 browser->top = nd;
2064 } while (offset != 0);
2065 } else if (offset < 0) {
2066 while (1) {
2067 h = rb_entry(nd, struct hist_entry, rb_node);
2068 if (h->unfolded && h->leaf) {
2069 if (first) {
2070 if (-offset > h->row_offset) {
2071 offset += h->row_offset;
2072 h->row_offset = 0;
2073 } else {
2074 h->row_offset += offset;
2075 offset = 0;
2076 browser->top = nd;
2077 break;
2079 } else {
2080 if (-offset > h->nr_rows) {
2081 offset += h->nr_rows;
2082 h->row_offset = 0;
2083 } else {
2084 h->row_offset = h->nr_rows + offset;
2085 offset = 0;
2086 browser->top = nd;
2087 break;
2092 nd = hists__filter_prev_entries(rb_hierarchy_prev(nd),
2093 hb->min_pcnt);
2094 if (nd == NULL)
2095 break;
2096 ++offset;
2097 browser->top = nd;
2098 if (offset == 0) {
2100 * Last unfiltered hist_entry, check if it is
2101 * unfolded, if it is then we should have
2102 * row_offset at its last entry.
2104 h = rb_entry(nd, struct hist_entry, rb_node);
2105 if (h->unfolded && h->leaf)
2106 h->row_offset = h->nr_rows;
2107 break;
2109 first = false;
2111 } else {
2112 browser->top = nd;
2113 h = rb_entry(nd, struct hist_entry, rb_node);
2114 h->row_offset = 0;
2118 static int hist_browser__fprintf_callchain(struct hist_browser *browser,
2119 struct hist_entry *he, FILE *fp,
2120 int level)
2122 struct callchain_print_arg arg = {
2123 .fp = fp,
2126 hist_browser__show_callchain(browser, he, level, 0,
2127 hist_browser__fprintf_callchain_entry, &arg,
2128 hist_browser__check_dump_full);
2129 return arg.printed;
2132 static int hist_browser__fprintf_entry(struct hist_browser *browser,
2133 struct hist_entry *he, FILE *fp)
2135 char s[8192];
2136 int printed = 0;
2137 char folded_sign = ' ';
2138 struct perf_hpp hpp = {
2139 .buf = s,
2140 .size = sizeof(s),
2142 struct perf_hpp_fmt *fmt;
2143 bool first = true;
2144 int ret;
2146 if (symbol_conf.use_callchain) {
2147 folded_sign = hist_entry__folded(he);
2148 printed += fprintf(fp, "%c ", folded_sign);
2151 hists__for_each_format(browser->hists, fmt) {
2152 if (perf_hpp__should_skip(fmt, he->hists))
2153 continue;
2155 if (!first) {
2156 ret = scnprintf(hpp.buf, hpp.size, " ");
2157 advance_hpp(&hpp, ret);
2158 } else
2159 first = false;
2161 ret = fmt->entry(fmt, &hpp, he);
2162 ret = hist_entry__snprintf_alignment(he, &hpp, fmt, ret);
2163 advance_hpp(&hpp, ret);
2165 printed += fprintf(fp, "%s\n", s);
2167 if (folded_sign == '-')
2168 printed += hist_browser__fprintf_callchain(browser, he, fp, 1);
2170 return printed;
2174 static int hist_browser__fprintf_hierarchy_entry(struct hist_browser *browser,
2175 struct hist_entry *he,
2176 FILE *fp, int level)
2178 char s[8192];
2179 int printed = 0;
2180 char folded_sign = ' ';
2181 struct perf_hpp hpp = {
2182 .buf = s,
2183 .size = sizeof(s),
2185 struct perf_hpp_fmt *fmt;
2186 struct perf_hpp_list_node *fmt_node;
2187 bool first = true;
2188 int ret;
2189 int hierarchy_indent = (he->hists->nr_hpp_node - 2) * HIERARCHY_INDENT;
2191 printed = fprintf(fp, "%*s", level * HIERARCHY_INDENT, "");
2193 folded_sign = hist_entry__folded(he);
2194 printed += fprintf(fp, "%c", folded_sign);
2196 /* the first hpp_list_node is for overhead columns */
2197 fmt_node = list_first_entry(&he->hists->hpp_formats,
2198 struct perf_hpp_list_node, list);
2199 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt) {
2200 if (!first) {
2201 ret = scnprintf(hpp.buf, hpp.size, " ");
2202 advance_hpp(&hpp, ret);
2203 } else
2204 first = false;
2206 ret = fmt->entry(fmt, &hpp, he);
2207 advance_hpp(&hpp, ret);
2210 ret = scnprintf(hpp.buf, hpp.size, "%*s", hierarchy_indent, "");
2211 advance_hpp(&hpp, ret);
2213 perf_hpp_list__for_each_format(he->hpp_list, fmt) {
2214 ret = scnprintf(hpp.buf, hpp.size, " ");
2215 advance_hpp(&hpp, ret);
2217 ret = fmt->entry(fmt, &hpp, he);
2218 advance_hpp(&hpp, ret);
2221 printed += fprintf(fp, "%s\n", rtrim(s));
2223 if (he->leaf && folded_sign == '-') {
2224 printed += hist_browser__fprintf_callchain(browser, he, fp,
2225 he->depth + 1);
2228 return printed;
2231 static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp)
2233 struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries),
2234 browser->min_pcnt);
2235 int printed = 0;
2237 while (nd) {
2238 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
2240 if (symbol_conf.report_hierarchy) {
2241 printed += hist_browser__fprintf_hierarchy_entry(browser,
2242 h, fp,
2243 h->depth);
2244 } else {
2245 printed += hist_browser__fprintf_entry(browser, h, fp);
2248 nd = hists__filter_entries(rb_hierarchy_next(nd),
2249 browser->min_pcnt);
2252 return printed;
2255 static int hist_browser__dump(struct hist_browser *browser)
2257 char filename[64];
2258 FILE *fp;
2260 while (1) {
2261 scnprintf(filename, sizeof(filename), "perf.hist.%d", browser->print_seq);
2262 if (access(filename, F_OK))
2263 break;
2265 * XXX: Just an arbitrary lazy upper limit
2267 if (++browser->print_seq == 8192) {
2268 ui_helpline__fpush("Too many perf.hist.N files, nothing written!");
2269 return -1;
2273 fp = fopen(filename, "w");
2274 if (fp == NULL) {
2275 char bf[64];
2276 const char *err = str_error_r(errno, bf, sizeof(bf));
2277 ui_helpline__fpush("Couldn't write to %s: %s", filename, err);
2278 return -1;
2281 ++browser->print_seq;
2282 hist_browser__fprintf(browser, fp);
2283 fclose(fp);
2284 ui_helpline__fpush("%s written!", filename);
2286 return 0;
2289 void hist_browser__init(struct hist_browser *browser,
2290 struct hists *hists)
2292 struct perf_hpp_fmt *fmt;
2294 browser->hists = hists;
2295 browser->b.refresh = hist_browser__refresh;
2296 browser->b.refresh_dimensions = hist_browser__refresh_dimensions;
2297 browser->b.seek = ui_browser__hists_seek;
2298 browser->b.use_navkeypressed = true;
2299 browser->show_headers = symbol_conf.show_hist_headers;
2301 if (symbol_conf.report_hierarchy) {
2302 struct perf_hpp_list_node *fmt_node;
2304 /* count overhead columns (in the first node) */
2305 fmt_node = list_first_entry(&hists->hpp_formats,
2306 struct perf_hpp_list_node, list);
2307 perf_hpp_list__for_each_format(&fmt_node->hpp, fmt)
2308 ++browser->b.columns;
2310 /* add a single column for whole hierarchy sort keys*/
2311 ++browser->b.columns;
2312 } else {
2313 hists__for_each_format(hists, fmt)
2314 ++browser->b.columns;
2317 hists__reset_column_width(hists);
2320 struct hist_browser *hist_browser__new(struct hists *hists)
2322 struct hist_browser *browser = zalloc(sizeof(*browser));
2324 if (browser)
2325 hist_browser__init(browser, hists);
2327 return browser;
2330 static struct hist_browser *
2331 perf_evsel_browser__new(struct perf_evsel *evsel,
2332 struct hist_browser_timer *hbt,
2333 struct perf_env *env)
2335 struct hist_browser *browser = hist_browser__new(evsel__hists(evsel));
2337 if (browser) {
2338 browser->hbt = hbt;
2339 browser->env = env;
2340 browser->title = perf_evsel_browser_title;
2342 return browser;
2345 void hist_browser__delete(struct hist_browser *browser)
2347 free(browser);
2350 static struct hist_entry *hist_browser__selected_entry(struct hist_browser *browser)
2352 return browser->he_selection;
2355 static struct thread *hist_browser__selected_thread(struct hist_browser *browser)
2357 return browser->he_selection->thread;
2360 /* Check whether the browser is for 'top' or 'report' */
2361 static inline bool is_report_browser(void *timer)
2363 return timer == NULL;
2366 static int perf_evsel_browser_title(struct hist_browser *browser,
2367 char *bf, size_t size)
2369 struct hist_browser_timer *hbt = browser->hbt;
2370 struct hists *hists = browser->hists;
2371 char unit;
2372 int printed;
2373 const struct dso *dso = hists->dso_filter;
2374 const struct thread *thread = hists->thread_filter;
2375 int socket_id = hists->socket_filter;
2376 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
2377 u64 nr_events = hists->stats.total_period;
2378 struct perf_evsel *evsel = hists_to_evsel(hists);
2379 const char *ev_name = perf_evsel__name(evsel);
2380 char buf[512];
2381 size_t buflen = sizeof(buf);
2382 char ref[30] = " show reference callgraph, ";
2383 bool enable_ref = false;
2385 if (symbol_conf.filter_relative) {
2386 nr_samples = hists->stats.nr_non_filtered_samples;
2387 nr_events = hists->stats.total_non_filtered_period;
2390 if (perf_evsel__is_group_event(evsel)) {
2391 struct perf_evsel *pos;
2393 perf_evsel__group_desc(evsel, buf, buflen);
2394 ev_name = buf;
2396 for_each_group_member(pos, evsel) {
2397 struct hists *pos_hists = evsel__hists(pos);
2399 if (symbol_conf.filter_relative) {
2400 nr_samples += pos_hists->stats.nr_non_filtered_samples;
2401 nr_events += pos_hists->stats.total_non_filtered_period;
2402 } else {
2403 nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
2404 nr_events += pos_hists->stats.total_period;
2409 if (symbol_conf.show_ref_callgraph &&
2410 strstr(ev_name, "call-graph=no"))
2411 enable_ref = true;
2412 nr_samples = convert_unit(nr_samples, &unit);
2413 printed = scnprintf(bf, size,
2414 "Samples: %lu%c of event '%s',%sEvent count (approx.): %" PRIu64,
2415 nr_samples, unit, ev_name, enable_ref ? ref : " ", nr_events);
2418 if (hists->uid_filter_str)
2419 printed += snprintf(bf + printed, size - printed,
2420 ", UID: %s", hists->uid_filter_str);
2421 if (thread) {
2422 if (hists__has(hists, thread)) {
2423 printed += scnprintf(bf + printed, size - printed,
2424 ", Thread: %s(%d)",
2425 (thread->comm_set ? thread__comm_str(thread) : ""),
2426 thread->tid);
2427 } else {
2428 printed += scnprintf(bf + printed, size - printed,
2429 ", Thread: %s",
2430 (thread->comm_set ? thread__comm_str(thread) : ""));
2433 if (dso)
2434 printed += scnprintf(bf + printed, size - printed,
2435 ", DSO: %s", dso->short_name);
2436 if (socket_id > -1)
2437 printed += scnprintf(bf + printed, size - printed,
2438 ", Processor Socket: %d", socket_id);
2439 if (!is_report_browser(hbt)) {
2440 struct perf_top *top = hbt->arg;
2442 if (top->zero)
2443 printed += scnprintf(bf + printed, size - printed, " [z]");
2446 return printed;
2449 static inline void free_popup_options(char **options, int n)
2451 int i;
2453 for (i = 0; i < n; ++i)
2454 zfree(&options[i]);
2458 * Only runtime switching of perf data file will make "input_name" point
2459 * to a malloced buffer. So add "is_input_name_malloced" flag to decide
2460 * whether we need to call free() for current "input_name" during the switch.
2462 static bool is_input_name_malloced = false;
2464 static int switch_data_file(void)
2466 char *pwd, *options[32], *abs_path[32], *tmp;
2467 DIR *pwd_dir;
2468 int nr_options = 0, choice = -1, ret = -1;
2469 struct dirent *dent;
2471 pwd = getenv("PWD");
2472 if (!pwd)
2473 return ret;
2475 pwd_dir = opendir(pwd);
2476 if (!pwd_dir)
2477 return ret;
2479 memset(options, 0, sizeof(options));
2480 memset(abs_path, 0, sizeof(abs_path));
2482 while ((dent = readdir(pwd_dir))) {
2483 char path[PATH_MAX];
2484 u64 magic;
2485 char *name = dent->d_name;
2486 FILE *file;
2488 if (!(dent->d_type == DT_REG))
2489 continue;
2491 snprintf(path, sizeof(path), "%s/%s", pwd, name);
2493 file = fopen(path, "r");
2494 if (!file)
2495 continue;
2497 if (fread(&magic, 1, 8, file) < 8)
2498 goto close_file_and_continue;
2500 if (is_perf_magic(magic)) {
2501 options[nr_options] = strdup(name);
2502 if (!options[nr_options])
2503 goto close_file_and_continue;
2505 abs_path[nr_options] = strdup(path);
2506 if (!abs_path[nr_options]) {
2507 zfree(&options[nr_options]);
2508 ui__warning("Can't search all data files due to memory shortage.\n");
2509 fclose(file);
2510 break;
2513 nr_options++;
2516 close_file_and_continue:
2517 fclose(file);
2518 if (nr_options >= 32) {
2519 ui__warning("Too many perf data files in PWD!\n"
2520 "Only the first 32 files will be listed.\n");
2521 break;
2524 closedir(pwd_dir);
2526 if (nr_options) {
2527 choice = ui__popup_menu(nr_options, options);
2528 if (choice < nr_options && choice >= 0) {
2529 tmp = strdup(abs_path[choice]);
2530 if (tmp) {
2531 if (is_input_name_malloced)
2532 free((void *)input_name);
2533 input_name = tmp;
2534 is_input_name_malloced = true;
2535 ret = 0;
2536 } else
2537 ui__warning("Data switch failed due to memory shortage!\n");
2541 free_popup_options(options, nr_options);
2542 free_popup_options(abs_path, nr_options);
2543 return ret;
2546 struct popup_action {
2547 struct thread *thread;
2548 struct map_symbol ms;
2549 int socket;
2551 int (*fn)(struct hist_browser *browser, struct popup_action *act);
2554 static int
2555 do_annotate(struct hist_browser *browser, struct popup_action *act)
2557 struct perf_evsel *evsel;
2558 struct annotation *notes;
2559 struct hist_entry *he;
2560 int err;
2562 if (!objdump_path && perf_env__lookup_objdump(browser->env))
2563 return 0;
2565 notes = symbol__annotation(act->ms.sym);
2566 if (!notes->src)
2567 return 0;
2569 evsel = hists_to_evsel(browser->hists);
2570 err = map_symbol__tui_annotate(&act->ms, evsel, browser->hbt);
2571 he = hist_browser__selected_entry(browser);
2573 * offer option to annotate the other branch source or target
2574 * (if they exists) when returning from annotate
2576 if ((err == 'q' || err == CTRL('c')) && he->branch_info)
2577 return 1;
2579 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
2580 if (err)
2581 ui_browser__handle_resize(&browser->b);
2582 return 0;
2585 static int
2586 add_annotate_opt(struct hist_browser *browser __maybe_unused,
2587 struct popup_action *act, char **optstr,
2588 struct map *map, struct symbol *sym)
2590 if (sym == NULL || map->dso->annotate_warned)
2591 return 0;
2593 if (asprintf(optstr, "Annotate %s", sym->name) < 0)
2594 return 0;
2596 act->ms.map = map;
2597 act->ms.sym = sym;
2598 act->fn = do_annotate;
2599 return 1;
2602 static int
2603 do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2605 struct thread *thread = act->thread;
2607 if ((!hists__has(browser->hists, thread) &&
2608 !hists__has(browser->hists, comm)) || thread == NULL)
2609 return 0;
2611 if (browser->hists->thread_filter) {
2612 pstack__remove(browser->pstack, &browser->hists->thread_filter);
2613 perf_hpp__set_elide(HISTC_THREAD, false);
2614 thread__zput(browser->hists->thread_filter);
2615 ui_helpline__pop();
2616 } else {
2617 if (hists__has(browser->hists, thread)) {
2618 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2619 thread->comm_set ? thread__comm_str(thread) : "",
2620 thread->tid);
2621 } else {
2622 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s thread\"",
2623 thread->comm_set ? thread__comm_str(thread) : "");
2626 browser->hists->thread_filter = thread__get(thread);
2627 perf_hpp__set_elide(HISTC_THREAD, false);
2628 pstack__push(browser->pstack, &browser->hists->thread_filter);
2631 hists__filter_by_thread(browser->hists);
2632 hist_browser__reset(browser);
2633 return 0;
2636 static int
2637 add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2638 char **optstr, struct thread *thread)
2640 int ret;
2642 if ((!hists__has(browser->hists, thread) &&
2643 !hists__has(browser->hists, comm)) || thread == NULL)
2644 return 0;
2646 if (hists__has(browser->hists, thread)) {
2647 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2648 browser->hists->thread_filter ? "out of" : "into",
2649 thread->comm_set ? thread__comm_str(thread) : "",
2650 thread->tid);
2651 } else {
2652 ret = asprintf(optstr, "Zoom %s %s thread",
2653 browser->hists->thread_filter ? "out of" : "into",
2654 thread->comm_set ? thread__comm_str(thread) : "");
2656 if (ret < 0)
2657 return 0;
2659 act->thread = thread;
2660 act->fn = do_zoom_thread;
2661 return 1;
2664 static int
2665 do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2667 struct map *map = act->ms.map;
2669 if (!hists__has(browser->hists, dso) || map == NULL)
2670 return 0;
2672 if (browser->hists->dso_filter) {
2673 pstack__remove(browser->pstack, &browser->hists->dso_filter);
2674 perf_hpp__set_elide(HISTC_DSO, false);
2675 browser->hists->dso_filter = NULL;
2676 ui_helpline__pop();
2677 } else {
2678 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s DSO\"",
2679 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name);
2680 browser->hists->dso_filter = map->dso;
2681 perf_hpp__set_elide(HISTC_DSO, true);
2682 pstack__push(browser->pstack, &browser->hists->dso_filter);
2685 hists__filter_by_dso(browser->hists);
2686 hist_browser__reset(browser);
2687 return 0;
2690 static int
2691 add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2692 char **optstr, struct map *map)
2694 if (!hists__has(browser->hists, dso) || map == NULL)
2695 return 0;
2697 if (asprintf(optstr, "Zoom %s %s DSO",
2698 browser->hists->dso_filter ? "out of" : "into",
2699 __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0)
2700 return 0;
2702 act->ms.map = map;
2703 act->fn = do_zoom_dso;
2704 return 1;
2707 static int
2708 do_browse_map(struct hist_browser *browser __maybe_unused,
2709 struct popup_action *act)
2711 map__browse(act->ms.map);
2712 return 0;
2715 static int
2716 add_map_opt(struct hist_browser *browser,
2717 struct popup_action *act, char **optstr, struct map *map)
2719 if (!hists__has(browser->hists, dso) || map == NULL)
2720 return 0;
2722 if (asprintf(optstr, "Browse map details") < 0)
2723 return 0;
2725 act->ms.map = map;
2726 act->fn = do_browse_map;
2727 return 1;
2730 static int
2731 do_run_script(struct hist_browser *browser __maybe_unused,
2732 struct popup_action *act)
2734 char script_opt[64];
2735 memset(script_opt, 0, sizeof(script_opt));
2737 if (act->thread) {
2738 scnprintf(script_opt, sizeof(script_opt), " -c %s ",
2739 thread__comm_str(act->thread));
2740 } else if (act->ms.sym) {
2741 scnprintf(script_opt, sizeof(script_opt), " -S %s ",
2742 act->ms.sym->name);
2745 script_browse(script_opt);
2746 return 0;
2749 static int
2750 add_script_opt(struct hist_browser *browser __maybe_unused,
2751 struct popup_action *act, char **optstr,
2752 struct thread *thread, struct symbol *sym)
2754 if (thread) {
2755 if (asprintf(optstr, "Run scripts for samples of thread [%s]",
2756 thread__comm_str(thread)) < 0)
2757 return 0;
2758 } else if (sym) {
2759 if (asprintf(optstr, "Run scripts for samples of symbol [%s]",
2760 sym->name) < 0)
2761 return 0;
2762 } else {
2763 if (asprintf(optstr, "Run scripts for all samples") < 0)
2764 return 0;
2767 act->thread = thread;
2768 act->ms.sym = sym;
2769 act->fn = do_run_script;
2770 return 1;
2773 static int
2774 do_switch_data(struct hist_browser *browser __maybe_unused,
2775 struct popup_action *act __maybe_unused)
2777 if (switch_data_file()) {
2778 ui__warning("Won't switch the data files due to\n"
2779 "no valid data file get selected!\n");
2780 return 0;
2783 return K_SWITCH_INPUT_DATA;
2786 static int
2787 add_switch_opt(struct hist_browser *browser,
2788 struct popup_action *act, char **optstr)
2790 if (!is_report_browser(browser->hbt))
2791 return 0;
2793 if (asprintf(optstr, "Switch to another data file in PWD") < 0)
2794 return 0;
2796 act->fn = do_switch_data;
2797 return 1;
2800 static int
2801 do_exit_browser(struct hist_browser *browser __maybe_unused,
2802 struct popup_action *act __maybe_unused)
2804 return 0;
2807 static int
2808 add_exit_opt(struct hist_browser *browser __maybe_unused,
2809 struct popup_action *act, char **optstr)
2811 if (asprintf(optstr, "Exit") < 0)
2812 return 0;
2814 act->fn = do_exit_browser;
2815 return 1;
2818 static int
2819 do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2821 if (!hists__has(browser->hists, socket) || act->socket < 0)
2822 return 0;
2824 if (browser->hists->socket_filter > -1) {
2825 pstack__remove(browser->pstack, &browser->hists->socket_filter);
2826 browser->hists->socket_filter = -1;
2827 perf_hpp__set_elide(HISTC_SOCKET, false);
2828 } else {
2829 browser->hists->socket_filter = act->socket;
2830 perf_hpp__set_elide(HISTC_SOCKET, true);
2831 pstack__push(browser->pstack, &browser->hists->socket_filter);
2834 hists__filter_by_socket(browser->hists);
2835 hist_browser__reset(browser);
2836 return 0;
2839 static int
2840 add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2841 char **optstr, int socket_id)
2843 if (!hists__has(browser->hists, socket) || socket_id < 0)
2844 return 0;
2846 if (asprintf(optstr, "Zoom %s Processor Socket %d",
2847 (browser->hists->socket_filter > -1) ? "out of" : "into",
2848 socket_id) < 0)
2849 return 0;
2851 act->socket = socket_id;
2852 act->fn = do_zoom_socket;
2853 return 1;
2856 static void hist_browser__update_nr_entries(struct hist_browser *hb)
2858 u64 nr_entries = 0;
2859 struct rb_node *nd = rb_first(&hb->hists->entries);
2861 if (hb->min_pcnt == 0 && !symbol_conf.report_hierarchy) {
2862 hb->nr_non_filtered_entries = hb->hists->nr_non_filtered_entries;
2863 return;
2866 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2867 nr_entries++;
2868 nd = rb_hierarchy_next(nd);
2871 hb->nr_non_filtered_entries = nr_entries;
2872 hb->nr_hierarchy_entries = nr_entries;
2875 static void hist_browser__update_percent_limit(struct hist_browser *hb,
2876 double percent)
2878 struct hist_entry *he;
2879 struct rb_node *nd = rb_first(&hb->hists->entries);
2880 u64 total = hists__total_period(hb->hists);
2881 u64 min_callchain_hits = total * (percent / 100);
2883 hb->min_pcnt = callchain_param.min_percent = percent;
2885 while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) {
2886 he = rb_entry(nd, struct hist_entry, rb_node);
2888 if (he->has_no_entry) {
2889 he->has_no_entry = false;
2890 he->nr_rows = 0;
2893 if (!he->leaf || !symbol_conf.use_callchain)
2894 goto next;
2896 if (callchain_param.mode == CHAIN_GRAPH_REL) {
2897 total = he->stat.period;
2899 if (symbol_conf.cumulate_callchain)
2900 total = he->stat_acc->period;
2902 min_callchain_hits = total * (percent / 100);
2905 callchain_param.sort(&he->sorted_chain, he->callchain,
2906 min_callchain_hits, &callchain_param);
2908 next:
2909 nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD);
2911 /* force to re-evaluate folding state of callchains */
2912 he->init_have_children = false;
2913 hist_entry__set_folding(he, hb, false);
2917 static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2918 const char *helpline,
2919 bool left_exits,
2920 struct hist_browser_timer *hbt,
2921 float min_pcnt,
2922 struct perf_env *env)
2924 struct hists *hists = evsel__hists(evsel);
2925 struct hist_browser *browser = perf_evsel_browser__new(evsel, hbt, env);
2926 struct branch_info *bi;
2927 #define MAX_OPTIONS 16
2928 char *options[MAX_OPTIONS];
2929 struct popup_action actions[MAX_OPTIONS];
2930 int nr_options = 0;
2931 int key = -1;
2932 char buf[64];
2933 int delay_secs = hbt ? hbt->refresh : 0;
2935 #define HIST_BROWSER_HELP_COMMON \
2936 "h/?/F1 Show this window\n" \
2937 "UP/DOWN/PGUP\n" \
2938 "PGDN/SPACE Navigate\n" \
2939 "q/ESC/CTRL+C Exit browser\n\n" \
2940 "For multiple event sessions:\n\n" \
2941 "TAB/UNTAB Switch events\n\n" \
2942 "For symbolic views (--sort has sym):\n\n" \
2943 "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \
2944 "ESC Zoom out\n" \
2945 "a Annotate current symbol\n" \
2946 "C Collapse all callchains\n" \
2947 "d Zoom into current DSO\n" \
2948 "E Expand all callchains\n" \
2949 "F Toggle percentage of filtered entries\n" \
2950 "H Display column headers\n" \
2951 "L Change percent limit\n" \
2952 "m Display context menu\n" \
2953 "S Zoom into current Processor Socket\n" \
2955 /* help messages are sorted by lexical order of the hotkey */
2956 const char report_help[] = HIST_BROWSER_HELP_COMMON
2957 "i Show header information\n"
2958 "P Print histograms to perf.hist.N\n"
2959 "r Run available scripts\n"
2960 "s Switch to another data file in PWD\n"
2961 "t Zoom into current Thread\n"
2962 "V Verbose (DSO names in callchains, etc)\n"
2963 "/ Filter symbol by name";
2964 const char top_help[] = HIST_BROWSER_HELP_COMMON
2965 "P Print histograms to perf.hist.N\n"
2966 "t Zoom into current Thread\n"
2967 "V Verbose (DSO names in callchains, etc)\n"
2968 "z Toggle zeroing of samples\n"
2969 "f Enable/Disable events\n"
2970 "/ Filter symbol by name";
2972 if (browser == NULL)
2973 return -1;
2975 /* reset abort key so that it can get Ctrl-C as a key */
2976 SLang_reset_tty();
2977 SLang_init_tty(0, 0, 0);
2979 if (min_pcnt)
2980 browser->min_pcnt = min_pcnt;
2981 hist_browser__update_nr_entries(browser);
2983 browser->pstack = pstack__new(3);
2984 if (browser->pstack == NULL)
2985 goto out;
2987 ui_helpline__push(helpline);
2989 memset(options, 0, sizeof(options));
2990 memset(actions, 0, sizeof(actions));
2992 if (symbol_conf.col_width_list_str)
2993 perf_hpp__set_user_width(symbol_conf.col_width_list_str);
2995 while (1) {
2996 struct thread *thread = NULL;
2997 struct map *map = NULL;
2998 int choice = 0;
2999 int socked_id = -1;
3001 nr_options = 0;
3003 key = hist_browser__run(browser, helpline);
3005 if (browser->he_selection != NULL) {
3006 thread = hist_browser__selected_thread(browser);
3007 map = browser->selection->map;
3008 socked_id = browser->he_selection->socket;
3010 switch (key) {
3011 case K_TAB:
3012 case K_UNTAB:
3013 if (nr_events == 1)
3014 continue;
3016 * Exit the browser, let hists__browser_tree
3017 * go to the next or previous
3019 goto out_free_stack;
3020 case 'a':
3021 if (!hists__has(hists, sym)) {
3022 ui_browser__warning(&browser->b, delay_secs * 2,
3023 "Annotation is only available for symbolic views, "
3024 "include \"sym*\" in --sort to use it.");
3025 continue;
3028 if (browser->selection == NULL ||
3029 browser->selection->sym == NULL ||
3030 browser->selection->map->dso->annotate_warned)
3031 continue;
3033 actions->ms.map = browser->selection->map;
3034 actions->ms.sym = browser->selection->sym;
3035 do_annotate(browser, actions);
3036 continue;
3037 case 'P':
3038 hist_browser__dump(browser);
3039 continue;
3040 case 'd':
3041 actions->ms.map = map;
3042 do_zoom_dso(browser, actions);
3043 continue;
3044 case 'V':
3045 verbose = (verbose + 1) % 4;
3046 browser->show_dso = verbose > 0;
3047 ui_helpline__fpush("Verbosity level set to %d\n",
3048 verbose);
3049 continue;
3050 case 't':
3051 actions->thread = thread;
3052 do_zoom_thread(browser, actions);
3053 continue;
3054 case 'S':
3055 actions->socket = socked_id;
3056 do_zoom_socket(browser, actions);
3057 continue;
3058 case '/':
3059 if (ui_browser__input_window("Symbol to show",
3060 "Please enter the name of symbol you want to see.\n"
3061 "To remove the filter later, press / + ENTER.",
3062 buf, "ENTER: OK, ESC: Cancel",
3063 delay_secs * 2) == K_ENTER) {
3064 hists->symbol_filter_str = *buf ? buf : NULL;
3065 hists__filter_by_symbol(hists);
3066 hist_browser__reset(browser);
3068 continue;
3069 case 'r':
3070 if (is_report_browser(hbt)) {
3071 actions->thread = NULL;
3072 actions->ms.sym = NULL;
3073 do_run_script(browser, actions);
3075 continue;
3076 case 's':
3077 if (is_report_browser(hbt)) {
3078 key = do_switch_data(browser, actions);
3079 if (key == K_SWITCH_INPUT_DATA)
3080 goto out_free_stack;
3082 continue;
3083 case 'i':
3084 /* env->arch is NULL for live-mode (i.e. perf top) */
3085 if (env->arch)
3086 tui__header_window(env);
3087 continue;
3088 case 'F':
3089 symbol_conf.filter_relative ^= 1;
3090 continue;
3091 case 'z':
3092 if (!is_report_browser(hbt)) {
3093 struct perf_top *top = hbt->arg;
3095 top->zero = !top->zero;
3097 continue;
3098 case 'L':
3099 if (ui_browser__input_window("Percent Limit",
3100 "Please enter the value you want to hide entries under that percent.",
3101 buf, "ENTER: OK, ESC: Cancel",
3102 delay_secs * 2) == K_ENTER) {
3103 char *end;
3104 double new_percent = strtod(buf, &end);
3106 if (new_percent < 0 || new_percent > 100) {
3107 ui_browser__warning(&browser->b, delay_secs * 2,
3108 "Invalid percent: %.2f", new_percent);
3109 continue;
3112 hist_browser__update_percent_limit(browser, new_percent);
3113 hist_browser__reset(browser);
3115 continue;
3116 case K_F1:
3117 case 'h':
3118 case '?':
3119 ui_browser__help_window(&browser->b,
3120 is_report_browser(hbt) ? report_help : top_help);
3121 continue;
3122 case K_ENTER:
3123 case K_RIGHT:
3124 case 'm':
3125 /* menu */
3126 break;
3127 case K_ESC:
3128 case K_LEFT: {
3129 const void *top;
3131 if (pstack__empty(browser->pstack)) {
3133 * Go back to the perf_evsel_menu__run or other user
3135 if (left_exits)
3136 goto out_free_stack;
3138 if (key == K_ESC &&
3139 ui_browser__dialog_yesno(&browser->b,
3140 "Do you really want to exit?"))
3141 goto out_free_stack;
3143 continue;
3145 top = pstack__peek(browser->pstack);
3146 if (top == &browser->hists->dso_filter) {
3148 * No need to set actions->dso here since
3149 * it's just to remove the current filter.
3150 * Ditto for thread below.
3152 do_zoom_dso(browser, actions);
3153 } else if (top == &browser->hists->thread_filter) {
3154 do_zoom_thread(browser, actions);
3155 } else if (top == &browser->hists->socket_filter) {
3156 do_zoom_socket(browser, actions);
3158 continue;
3160 case 'q':
3161 case CTRL('c'):
3162 goto out_free_stack;
3163 case 'f':
3164 if (!is_report_browser(hbt)) {
3165 struct perf_top *top = hbt->arg;
3167 perf_evlist__toggle_enable(top->evlist);
3169 * No need to refresh, resort/decay histogram
3170 * entries if we are not collecting samples:
3172 if (top->evlist->enabled) {
3173 helpline = "Press 'f' to disable the events or 'h' to see other hotkeys";
3174 hbt->refresh = delay_secs;
3175 } else {
3176 helpline = "Press 'f' again to re-enable the events";
3177 hbt->refresh = 0;
3179 continue;
3181 /* Fall thru */
3182 default:
3183 helpline = "Press '?' for help on key bindings";
3184 continue;
3187 if (!hists__has(hists, sym) || browser->selection == NULL)
3188 goto skip_annotation;
3190 if (sort__mode == SORT_MODE__BRANCH) {
3191 bi = browser->he_selection->branch_info;
3193 if (bi == NULL)
3194 goto skip_annotation;
3196 nr_options += add_annotate_opt(browser,
3197 &actions[nr_options],
3198 &options[nr_options],
3199 bi->from.map,
3200 bi->from.sym);
3201 if (bi->to.sym != bi->from.sym)
3202 nr_options += add_annotate_opt(browser,
3203 &actions[nr_options],
3204 &options[nr_options],
3205 bi->to.map,
3206 bi->to.sym);
3207 } else {
3208 nr_options += add_annotate_opt(browser,
3209 &actions[nr_options],
3210 &options[nr_options],
3211 browser->selection->map,
3212 browser->selection->sym);
3214 skip_annotation:
3215 nr_options += add_thread_opt(browser, &actions[nr_options],
3216 &options[nr_options], thread);
3217 nr_options += add_dso_opt(browser, &actions[nr_options],
3218 &options[nr_options], map);
3219 nr_options += add_map_opt(browser, &actions[nr_options],
3220 &options[nr_options],
3221 browser->selection ?
3222 browser->selection->map : NULL);
3223 nr_options += add_socket_opt(browser, &actions[nr_options],
3224 &options[nr_options],
3225 socked_id);
3226 /* perf script support */
3227 if (!is_report_browser(hbt))
3228 goto skip_scripting;
3230 if (browser->he_selection) {
3231 if (hists__has(hists, thread) && thread) {
3232 nr_options += add_script_opt(browser,
3233 &actions[nr_options],
3234 &options[nr_options],
3235 thread, NULL);
3238 * Note that browser->selection != NULL
3239 * when browser->he_selection is not NULL,
3240 * so we don't need to check browser->selection
3241 * before fetching browser->selection->sym like what
3242 * we do before fetching browser->selection->map.
3244 * See hist_browser__show_entry.
3246 if (hists__has(hists, sym) && browser->selection->sym) {
3247 nr_options += add_script_opt(browser,
3248 &actions[nr_options],
3249 &options[nr_options],
3250 NULL, browser->selection->sym);
3253 nr_options += add_script_opt(browser, &actions[nr_options],
3254 &options[nr_options], NULL, NULL);
3255 nr_options += add_switch_opt(browser, &actions[nr_options],
3256 &options[nr_options]);
3257 skip_scripting:
3258 nr_options += add_exit_opt(browser, &actions[nr_options],
3259 &options[nr_options]);
3261 do {
3262 struct popup_action *act;
3264 choice = ui__popup_menu(nr_options, options);
3265 if (choice == -1 || choice >= nr_options)
3266 break;
3268 act = &actions[choice];
3269 key = act->fn(browser, act);
3270 } while (key == 1);
3272 if (key == K_SWITCH_INPUT_DATA)
3273 break;
3275 out_free_stack:
3276 pstack__delete(browser->pstack);
3277 out:
3278 hist_browser__delete(browser);
3279 free_popup_options(options, MAX_OPTIONS);
3280 return key;
3283 struct perf_evsel_menu {
3284 struct ui_browser b;
3285 struct perf_evsel *selection;
3286 bool lost_events, lost_events_warned;
3287 float min_pcnt;
3288 struct perf_env *env;
3291 static void perf_evsel_menu__write(struct ui_browser *browser,
3292 void *entry, int row)
3294 struct perf_evsel_menu *menu = container_of(browser,
3295 struct perf_evsel_menu, b);
3296 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3297 struct hists *hists = evsel__hists(evsel);
3298 bool current_entry = ui_browser__is_current_entry(browser, row);
3299 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
3300 const char *ev_name = perf_evsel__name(evsel);
3301 char bf[256], unit;
3302 const char *warn = " ";
3303 size_t printed;
3305 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
3306 HE_COLORSET_NORMAL);
3308 if (perf_evsel__is_group_event(evsel)) {
3309 struct perf_evsel *pos;
3311 ev_name = perf_evsel__group_name(evsel);
3313 for_each_group_member(pos, evsel) {
3314 struct hists *pos_hists = evsel__hists(pos);
3315 nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
3319 nr_events = convert_unit(nr_events, &unit);
3320 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
3321 unit, unit == ' ' ? "" : " ", ev_name);
3322 ui_browser__printf(browser, "%s", bf);
3324 nr_events = hists->stats.nr_events[PERF_RECORD_LOST];
3325 if (nr_events != 0) {
3326 menu->lost_events = true;
3327 if (!current_entry)
3328 ui_browser__set_color(browser, HE_COLORSET_TOP);
3329 nr_events = convert_unit(nr_events, &unit);
3330 printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!",
3331 nr_events, unit, unit == ' ' ? "" : " ");
3332 warn = bf;
3335 ui_browser__write_nstring(browser, warn, browser->width - printed);
3337 if (current_entry)
3338 menu->selection = evsel;
3341 static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
3342 int nr_events, const char *help,
3343 struct hist_browser_timer *hbt)
3345 struct perf_evlist *evlist = menu->b.priv;
3346 struct perf_evsel *pos;
3347 const char *title = "Available samples";
3348 int delay_secs = hbt ? hbt->refresh : 0;
3349 int key;
3351 if (ui_browser__show(&menu->b, title,
3352 "ESC: exit, ENTER|->: Browse histograms") < 0)
3353 return -1;
3355 while (1) {
3356 key = ui_browser__run(&menu->b, delay_secs);
3358 switch (key) {
3359 case K_TIMER:
3360 hbt->timer(hbt->arg);
3362 if (!menu->lost_events_warned && menu->lost_events) {
3363 ui_browser__warn_lost_events(&menu->b);
3364 menu->lost_events_warned = true;
3366 continue;
3367 case K_RIGHT:
3368 case K_ENTER:
3369 if (!menu->selection)
3370 continue;
3371 pos = menu->selection;
3372 browse_hists:
3373 perf_evlist__set_selected(evlist, pos);
3375 * Give the calling tool a chance to populate the non
3376 * default evsel resorted hists tree.
3378 if (hbt)
3379 hbt->timer(hbt->arg);
3380 key = perf_evsel__hists_browse(pos, nr_events, help,
3381 true, hbt,
3382 menu->min_pcnt,
3383 menu->env);
3384 ui_browser__show_title(&menu->b, title);
3385 switch (key) {
3386 case K_TAB:
3387 if (pos->node.next == &evlist->entries)
3388 pos = perf_evlist__first(evlist);
3389 else
3390 pos = perf_evsel__next(pos);
3391 goto browse_hists;
3392 case K_UNTAB:
3393 if (pos->node.prev == &evlist->entries)
3394 pos = perf_evlist__last(evlist);
3395 else
3396 pos = perf_evsel__prev(pos);
3397 goto browse_hists;
3398 case K_SWITCH_INPUT_DATA:
3399 case 'q':
3400 case CTRL('c'):
3401 goto out;
3402 case K_ESC:
3403 default:
3404 continue;
3406 case K_LEFT:
3407 continue;
3408 case K_ESC:
3409 if (!ui_browser__dialog_yesno(&menu->b,
3410 "Do you really want to exit?"))
3411 continue;
3412 /* Fall thru */
3413 case 'q':
3414 case CTRL('c'):
3415 goto out;
3416 default:
3417 continue;
3421 out:
3422 ui_browser__hide(&menu->b);
3423 return key;
3426 static bool filter_group_entries(struct ui_browser *browser __maybe_unused,
3427 void *entry)
3429 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
3431 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
3432 return true;
3434 return false;
3437 static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
3438 int nr_entries, const char *help,
3439 struct hist_browser_timer *hbt,
3440 float min_pcnt,
3441 struct perf_env *env)
3443 struct perf_evsel *pos;
3444 struct perf_evsel_menu menu = {
3445 .b = {
3446 .entries = &evlist->entries,
3447 .refresh = ui_browser__list_head_refresh,
3448 .seek = ui_browser__list_head_seek,
3449 .write = perf_evsel_menu__write,
3450 .filter = filter_group_entries,
3451 .nr_entries = nr_entries,
3452 .priv = evlist,
3454 .min_pcnt = min_pcnt,
3455 .env = env,
3458 ui_helpline__push("Press ESC to exit");
3460 evlist__for_each_entry(evlist, pos) {
3461 const char *ev_name = perf_evsel__name(pos);
3462 size_t line_len = strlen(ev_name) + 7;
3464 if (menu.b.width < line_len)
3465 menu.b.width = line_len;
3468 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
3471 int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
3472 struct hist_browser_timer *hbt,
3473 float min_pcnt,
3474 struct perf_env *env)
3476 int nr_entries = evlist->nr_entries;
3478 single_entry:
3479 if (nr_entries == 1) {
3480 struct perf_evsel *first = perf_evlist__first(evlist);
3482 return perf_evsel__hists_browse(first, nr_entries, help,
3483 false, hbt, min_pcnt,
3484 env);
3487 if (symbol_conf.event_group) {
3488 struct perf_evsel *pos;
3490 nr_entries = 0;
3491 evlist__for_each_entry(evlist, pos) {
3492 if (perf_evsel__is_group_leader(pos))
3493 nr_entries++;
3496 if (nr_entries == 1)
3497 goto single_entry;
3500 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
3501 hbt, min_pcnt, env);