diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 82 |
1 files changed, 71 insertions, 11 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 28d1cb2127e9..ed391db9e0f8 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "util/rbtree.h" | 15 | #include "util/rbtree.h" |
16 | #include "util/symbol.h" | 16 | #include "util/symbol.h" |
17 | #include "util/string.h" | 17 | #include "util/string.h" |
18 | #include "util/callchain.h" | ||
18 | 19 | ||
19 | #include "perf.h" | 20 | #include "perf.h" |
20 | #include "util/header.h" | 21 | #include "util/header.h" |
@@ -52,6 +53,7 @@ static char *parent_pattern = default_parent_pattern; | |||
52 | static regex_t parent_regex; | 53 | static regex_t parent_regex; |
53 | 54 | ||
54 | static int exclude_other = 1; | 55 | static int exclude_other = 1; |
56 | static int callchain; | ||
55 | 57 | ||
56 | static u64 sample_type; | 58 | static u64 sample_type; |
57 | 59 | ||
@@ -488,17 +490,19 @@ static size_t threads__fprintf(FILE *fp) | |||
488 | static struct rb_root hist; | 490 | static struct rb_root hist; |
489 | 491 | ||
490 | struct hist_entry { | 492 | struct hist_entry { |
491 | struct rb_node rb_node; | 493 | struct rb_node rb_node; |
492 | 494 | ||
493 | struct thread *thread; | 495 | struct thread *thread; |
494 | struct map *map; | 496 | struct map *map; |
495 | struct dso *dso; | 497 | struct dso *dso; |
496 | struct symbol *sym; | 498 | struct symbol *sym; |
497 | struct symbol *parent; | 499 | struct symbol *parent; |
498 | u64 ip; | 500 | u64 ip; |
499 | char level; | 501 | char level; |
500 | 502 | struct callchain_node callchain; | |
501 | u64 count; | 503 | struct rb_root sorted_chain; |
504 | |||
505 | u64 count; | ||
502 | }; | 506 | }; |
503 | 507 | ||
504 | /* | 508 | /* |
@@ -769,6 +773,48 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
769 | } | 773 | } |
770 | 774 | ||
771 | static size_t | 775 | static size_t |
776 | callchain__fprintf(FILE *fp, struct callchain_node *self, u64 total_samples) | ||
777 | { | ||
778 | struct callchain_list *chain; | ||
779 | size_t ret = 0; | ||
780 | |||
781 | if (!self) | ||
782 | return 0; | ||
783 | |||
784 | ret += callchain__fprintf(fp, self->parent, total_samples); | ||
785 | |||
786 | |||
787 | list_for_each_entry(chain, &self->val, list) | ||
788 | ret += fprintf(fp, " %p\n", (void *)chain->ip); | ||
789 | |||
790 | return ret; | ||
791 | } | ||
792 | |||
793 | static size_t | ||
794 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
795 | u64 total_samples) | ||
796 | { | ||
797 | struct rb_node *rb_node; | ||
798 | struct callchain_node *chain; | ||
799 | size_t ret = 0; | ||
800 | |||
801 | rb_node = rb_first(&self->sorted_chain); | ||
802 | while (rb_node) { | ||
803 | double percent; | ||
804 | |||
805 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
806 | percent = chain->hit * 100.0 / total_samples; | ||
807 | ret += fprintf(fp, " %6.2f%%\n", percent); | ||
808 | ret += callchain__fprintf(fp, chain, total_samples); | ||
809 | ret += fprintf(fp, "\n"); | ||
810 | rb_node = rb_next(rb_node); | ||
811 | } | ||
812 | |||
813 | return ret; | ||
814 | } | ||
815 | |||
816 | |||
817 | static size_t | ||
772 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | 818 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) |
773 | { | 819 | { |
774 | struct sort_entry *se; | 820 | struct sort_entry *se; |
@@ -808,6 +854,9 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
808 | 854 | ||
809 | ret += fprintf(fp, "\n"); | 855 | ret += fprintf(fp, "\n"); |
810 | 856 | ||
857 | if (callchain) | ||
858 | hist_entry_callchain__fprintf(fp, self, total_samples); | ||
859 | |||
811 | return ret; | 860 | return ret; |
812 | } | 861 | } |
813 | 862 | ||
@@ -892,6 +941,7 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
892 | .level = level, | 941 | .level = level, |
893 | .count = count, | 942 | .count = count, |
894 | .parent = NULL, | 943 | .parent = NULL, |
944 | .sorted_chain = RB_ROOT | ||
895 | }; | 945 | }; |
896 | int cmp; | 946 | int cmp; |
897 | 947 | ||
@@ -934,6 +984,8 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
934 | 984 | ||
935 | if (!cmp) { | 985 | if (!cmp) { |
936 | he->count += count; | 986 | he->count += count; |
987 | if (callchain) | ||
988 | append_chain(&he->callchain, chain); | ||
937 | return 0; | 989 | return 0; |
938 | } | 990 | } |
939 | 991 | ||
@@ -947,6 +999,10 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |||
947 | if (!he) | 999 | if (!he) |
948 | return -ENOMEM; | 1000 | return -ENOMEM; |
949 | *he = entry; | 1001 | *he = entry; |
1002 | if (callchain) { | ||
1003 | callchain_init(&he->callchain); | ||
1004 | append_chain(&he->callchain, chain); | ||
1005 | } | ||
950 | rb_link_node(&he->rb_node, parent, p); | 1006 | rb_link_node(&he->rb_node, parent, p); |
951 | rb_insert_color(&he->rb_node, &hist); | 1007 | rb_insert_color(&he->rb_node, &hist); |
952 | 1008 | ||
@@ -1023,6 +1079,9 @@ static void output__insert_entry(struct hist_entry *he) | |||
1023 | struct rb_node *parent = NULL; | 1079 | struct rb_node *parent = NULL; |
1024 | struct hist_entry *iter; | 1080 | struct hist_entry *iter; |
1025 | 1081 | ||
1082 | if (callchain) | ||
1083 | sort_chain_to_rbtree(&he->sorted_chain, &he->callchain); | ||
1084 | |||
1026 | while (*p != NULL) { | 1085 | while (*p != NULL) { |
1027 | parent = *p; | 1086 | parent = *p; |
1028 | iter = rb_entry(parent, struct hist_entry, rb_node); | 1087 | iter = rb_entry(parent, struct hist_entry, rb_node); |
@@ -1599,6 +1658,7 @@ static const struct option options[] = { | |||
1599 | "regex filter to identify parent, see: '--sort parent'"), | 1658 | "regex filter to identify parent, see: '--sort parent'"), |
1600 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 1659 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1601 | "Only display entries with parent-match"), | 1660 | "Only display entries with parent-match"), |
1661 | OPT_BOOLEAN('c', "callchain", &callchain, "Display callchains"), | ||
1602 | OPT_END() | 1662 | OPT_END() |
1603 | }; | 1663 | }; |
1604 | 1664 | ||