diff options
author | Namhyung Kim <namhyung@kernel.org> | 2015-11-09 00:45:44 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-11-19 11:19:25 -0500 |
commit | 8c430a34869946f1f5852f02d910ceef80040be5 (patch) | |
tree | d7255091acd8abec98ed884d67423ad81d7bca6e | |
parent | 4b3a3212233a042f48b7b8fedc64933e1ccd8643 (diff) |
perf hists browser: Support folded callchains
The folded callchain mode prints all chains in a single line.
Currently perf report --tui doesn't support folded callchains. Like
flat callchains, only leaf nodes are added to the final rbtree so it
should show entries in parent nodes. To do that, add flat_val list to
struct callchain_node and show them along with the (normal) val list.
For example, folded callchain looks like below:
$ perf report -g folded --tui
Samples: 234 of event 'cycles:pp', Event count (approx.): 32605268
Overhead Command Shared Object Symbol
- 39.93% swapper [kernel.vmlinux] [k] intel_idle
+ 28.63% intel_idle; cpuidle_enter_state; cpuidle_enter; ...
+ 11.30% intel_idle; cpuidle_enter_state; cpuidle_enter; ...
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1447047946-1691-9-git-send-email-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 125 |
1 files changed, 124 insertions, 1 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index c44af461a68f..a211b7b6a81e 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -207,6 +207,11 @@ static int callchain_node__count_flat_rows(struct callchain_node *node) | |||
207 | return n; | 207 | return n; |
208 | } | 208 | } |
209 | 209 | ||
210 | static int callchain_node__count_folded_rows(struct callchain_node *node __maybe_unused) | ||
211 | { | ||
212 | return 1; | ||
213 | } | ||
214 | |||
210 | static int callchain_node__count_rows(struct callchain_node *node) | 215 | static int callchain_node__count_rows(struct callchain_node *node) |
211 | { | 216 | { |
212 | struct callchain_list *chain; | 217 | struct callchain_list *chain; |
@@ -215,6 +220,8 @@ static int callchain_node__count_rows(struct callchain_node *node) | |||
215 | 220 | ||
216 | if (callchain_param.mode == CHAIN_FLAT) | 221 | if (callchain_param.mode == CHAIN_FLAT) |
217 | return callchain_node__count_flat_rows(node); | 222 | return callchain_node__count_flat_rows(node); |
223 | else if (callchain_param.mode == CHAIN_FOLDED) | ||
224 | return callchain_node__count_folded_rows(node); | ||
218 | 225 | ||
219 | list_for_each_entry(chain, &node->val, list) { | 226 | list_for_each_entry(chain, &node->val, list) { |
220 | ++n; | 227 | ++n; |
@@ -311,7 +318,8 @@ static void callchain__init_have_children(struct rb_root *root) | |||
311 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | 318 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { |
312 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | 319 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); |
313 | callchain_node__init_have_children(node, has_sibling); | 320 | callchain_node__init_have_children(node, has_sibling); |
314 | if (callchain_param.mode == CHAIN_FLAT) | 321 | if (callchain_param.mode == CHAIN_FLAT || |
322 | callchain_param.mode == CHAIN_FOLDED) | ||
315 | callchain_node__make_parent_list(node); | 323 | callchain_node__make_parent_list(node); |
316 | } | 324 | } |
317 | } | 325 | } |
@@ -723,6 +731,116 @@ out: | |||
723 | return row - first_row; | 731 | return row - first_row; |
724 | } | 732 | } |
725 | 733 | ||
734 | static char *hist_browser__folded_callchain_str(struct hist_browser *browser, | ||
735 | struct callchain_list *chain, | ||
736 | char *value_str, char *old_str) | ||
737 | { | ||
738 | char bf[1024]; | ||
739 | const char *str; | ||
740 | char *new; | ||
741 | |||
742 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | ||
743 | browser->show_dso); | ||
744 | if (old_str) { | ||
745 | if (asprintf(&new, "%s%s%s", old_str, | ||
746 | symbol_conf.field_sep ?: ";", str) < 0) | ||
747 | new = NULL; | ||
748 | } else { | ||
749 | if (value_str) { | ||
750 | if (asprintf(&new, "%s %s", value_str, str) < 0) | ||
751 | new = NULL; | ||
752 | } else { | ||
753 | if (asprintf(&new, "%s", str) < 0) | ||
754 | new = NULL; | ||
755 | } | ||
756 | } | ||
757 | return new; | ||
758 | } | ||
759 | |||
760 | static int hist_browser__show_callchain_folded(struct hist_browser *browser, | ||
761 | struct rb_root *root, | ||
762 | unsigned short row, u64 total, | ||
763 | print_callchain_entry_fn print, | ||
764 | struct callchain_print_arg *arg, | ||
765 | check_output_full_fn is_output_full) | ||
766 | { | ||
767 | struct rb_node *node; | ||
768 | int first_row = row, offset = LEVEL_OFFSET_STEP; | ||
769 | bool need_percent; | ||
770 | |||
771 | node = rb_first(root); | ||
772 | need_percent = node && rb_next(node); | ||
773 | |||
774 | while (node) { | ||
775 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
776 | struct rb_node *next = rb_next(node); | ||
777 | struct callchain_list *chain, *first_chain = NULL; | ||
778 | int first = true; | ||
779 | char *value_str = NULL, *value_str_alloc = NULL; | ||
780 | char *chain_str = NULL, *chain_str_alloc = NULL; | ||
781 | |||
782 | if (arg->row_offset != 0) { | ||
783 | arg->row_offset--; | ||
784 | goto next; | ||
785 | } | ||
786 | |||
787 | if (need_percent) { | ||
788 | char buf[64]; | ||
789 | |||
790 | callchain_node__scnprintf_value(child, buf, sizeof(buf), total); | ||
791 | if (asprintf(&value_str, "%s", buf) < 0) { | ||
792 | value_str = (char *)"<...>"; | ||
793 | goto do_print; | ||
794 | } | ||
795 | value_str_alloc = value_str; | ||
796 | } | ||
797 | |||
798 | list_for_each_entry(chain, &child->parent_val, list) { | ||
799 | chain_str = hist_browser__folded_callchain_str(browser, | ||
800 | chain, value_str, chain_str); | ||
801 | if (first) { | ||
802 | first = false; | ||
803 | first_chain = chain; | ||
804 | } | ||
805 | |||
806 | if (chain_str == NULL) { | ||
807 | chain_str = (char *)"Not enough memory!"; | ||
808 | goto do_print; | ||
809 | } | ||
810 | |||
811 | chain_str_alloc = chain_str; | ||
812 | } | ||
813 | |||
814 | list_for_each_entry(chain, &child->val, list) { | ||
815 | chain_str = hist_browser__folded_callchain_str(browser, | ||
816 | chain, value_str, chain_str); | ||
817 | if (first) { | ||
818 | first = false; | ||
819 | first_chain = chain; | ||
820 | } | ||
821 | |||
822 | if (chain_str == NULL) { | ||
823 | chain_str = (char *)"Not enough memory!"; | ||
824 | goto do_print; | ||
825 | } | ||
826 | |||
827 | chain_str_alloc = chain_str; | ||
828 | } | ||
829 | |||
830 | do_print: | ||
831 | print(browser, first_chain, chain_str, offset, row++, arg); | ||
832 | free(value_str_alloc); | ||
833 | free(chain_str_alloc); | ||
834 | |||
835 | next: | ||
836 | if (is_output_full(browser, row)) | ||
837 | break; | ||
838 | node = next; | ||
839 | } | ||
840 | |||
841 | return row - first_row; | ||
842 | } | ||
843 | |||
726 | static int hist_browser__show_callchain(struct hist_browser *browser, | 844 | static int hist_browser__show_callchain(struct hist_browser *browser, |
727 | struct rb_root *root, int level, | 845 | struct rb_root *root, int level, |
728 | unsigned short row, u64 total, | 846 | unsigned short row, u64 total, |
@@ -980,6 +1098,11 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
980 | &entry->sorted_chain, row, total, | 1098 | &entry->sorted_chain, row, total, |
981 | hist_browser__show_callchain_entry, &arg, | 1099 | hist_browser__show_callchain_entry, &arg, |
982 | hist_browser__check_output_full); | 1100 | hist_browser__check_output_full); |
1101 | } else if (callchain_param.mode == CHAIN_FOLDED) { | ||
1102 | printed += hist_browser__show_callchain_folded(browser, | ||
1103 | &entry->sorted_chain, row, total, | ||
1104 | hist_browser__show_callchain_entry, &arg, | ||
1105 | hist_browser__check_output_full); | ||
983 | } else { | 1106 | } else { |
984 | printed += hist_browser__show_callchain(browser, | 1107 | printed += hist_browser__show_callchain(browser, |
985 | &entry->sorted_chain, 1, row, total, | 1108 | &entry->sorted_chain, 1, row, total, |