diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2009-06-26 10:28:01 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-26 10:47:01 -0400 |
commit | f55c555226b1010b249730ec6b232e5470286950 (patch) | |
tree | 27523398f7d2487e06532245cc6ed745a611ccf0 /tools/perf/builtin-report.c | |
parent | 8cb76d99d715741637b6d0884f389e17e9cb05d2 (diff) |
perf report: Print sorted callchains per histogram entries
Use the newly created callchains radix tree to gather the chains stats
from the recorded events and then print the callchains for all of them,
sorted by hits, using the "-c" parameter with perf report.
Example:
66.15% [k] atm_clip_exit
63.08%
0xffffffffffffff80
0xffffffff810196a8
0xffffffff810c14c8
0xffffffff8101a79c
0xffffffff810194f3
0xffffffff8106ab7f
0xffffffff8106abe5
0xffffffff8106acde
0xffffffff8100d94b
0xffffffff8153e7ea
[...]
1.54%
0xffffffffffffff80
0xffffffff810196a8
0xffffffff810c14c8
0xffffffff8101a79c
[...]
Symbols are not yet resolved.
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
LKML-Reference: <1246026481-8314-3-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
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 | ||