diff options
| -rw-r--r-- | tools/perf/builtin-report.c | 60 | ||||
| -rw-r--r-- | tools/perf/util/callchain.c | 84 | ||||
| -rw-r--r-- | tools/perf/util/callchain.h | 21 |
3 files changed, 124 insertions, 41 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8bd58651128c..4e5cc266311e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -59,10 +59,15 @@ static regex_t parent_regex; | |||
| 59 | 59 | ||
| 60 | static int exclude_other = 1; | 60 | static int exclude_other = 1; |
| 61 | 61 | ||
| 62 | static char callchain_default_opt[] = "graph,0.5"; | 62 | static char callchain_default_opt[] = "fractal,0.5"; |
| 63 | |||
| 63 | static int callchain; | 64 | static int callchain; |
| 64 | static enum chain_mode callchain_mode; | 65 | |
| 65 | static double callchain_min_percent = 0.5; | 66 | static |
| 67 | struct callchain_param callchain_param = { | ||
| 68 | .mode = CHAIN_GRAPH_ABS, | ||
| 69 | .min_percent = 0.5 | ||
| 70 | }; | ||
| 66 | 71 | ||
| 67 | static u64 sample_type; | 72 | static u64 sample_type; |
| 68 | 73 | ||
| @@ -846,9 +851,15 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 846 | struct callchain_node *child; | 851 | struct callchain_node *child; |
| 847 | struct callchain_list *chain; | 852 | struct callchain_list *chain; |
| 848 | int new_depth_mask = depth_mask; | 853 | int new_depth_mask = depth_mask; |
| 854 | u64 new_total; | ||
| 849 | size_t ret = 0; | 855 | size_t ret = 0; |
| 850 | int i; | 856 | int i; |
| 851 | 857 | ||
| 858 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 859 | new_total = self->cumul_hit; | ||
| 860 | else | ||
| 861 | new_total = total_samples; | ||
| 862 | |||
| 852 | node = rb_first(&self->rb_root); | 863 | node = rb_first(&self->rb_root); |
| 853 | while (node) { | 864 | while (node) { |
| 854 | child = rb_entry(node, struct callchain_node, rb_node); | 865 | child = rb_entry(node, struct callchain_node, rb_node); |
| @@ -873,10 +884,10 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 873 | continue; | 884 | continue; |
| 874 | ret += ipchain__fprintf_graph(fp, chain, depth, | 885 | ret += ipchain__fprintf_graph(fp, chain, depth, |
| 875 | new_depth_mask, i++, | 886 | new_depth_mask, i++, |
| 876 | total_samples, | 887 | new_total, |
| 877 | child->cumul_hit); | 888 | child->cumul_hit); |
| 878 | } | 889 | } |
| 879 | ret += callchain__fprintf_graph(fp, child, total_samples, | 890 | ret += callchain__fprintf_graph(fp, child, new_total, |
| 880 | depth + 1, | 891 | depth + 1, |
| 881 | new_depth_mask | (1 << depth)); | 892 | new_depth_mask | (1 << depth)); |
| 882 | node = next; | 893 | node = next; |
| @@ -925,13 +936,18 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
| 925 | 936 | ||
| 926 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 937 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
| 927 | percent = chain->hit * 100.0 / total_samples; | 938 | percent = chain->hit * 100.0 / total_samples; |
| 928 | if (callchain_mode == FLAT) { | 939 | switch (callchain_param.mode) { |
| 940 | case CHAIN_FLAT: | ||
| 929 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | 941 | ret += percent_color_fprintf(fp, " %6.2f%%\n", |
| 930 | percent); | 942 | percent); |
| 931 | ret += callchain__fprintf_flat(fp, chain, total_samples); | 943 | ret += callchain__fprintf_flat(fp, chain, total_samples); |
| 932 | } else if (callchain_mode == GRAPH) { | 944 | break; |
| 945 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 946 | case CHAIN_GRAPH_REL: | ||
| 933 | ret += callchain__fprintf_graph(fp, chain, | 947 | ret += callchain__fprintf_graph(fp, chain, |
| 934 | total_samples, 1, 1); | 948 | total_samples, 1, 1); |
| 949 | default: | ||
| 950 | break; | ||
| 935 | } | 951 | } |
| 936 | ret += fprintf(fp, "\n"); | 952 | ret += fprintf(fp, "\n"); |
| 937 | rb_node = rb_next(rb_node); | 953 | rb_node = rb_next(rb_node); |
| @@ -1219,14 +1235,9 @@ static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | |||
| 1219 | struct rb_node *parent = NULL; | 1235 | struct rb_node *parent = NULL; |
| 1220 | struct hist_entry *iter; | 1236 | struct hist_entry *iter; |
| 1221 | 1237 | ||
| 1222 | if (callchain) { | 1238 | if (callchain) |
| 1223 | if (callchain_mode == FLAT) | 1239 | callchain_param.sort(&he->sorted_chain, &he->callchain, |
| 1224 | sort_chain_flat(&he->sorted_chain, &he->callchain, | 1240 | min_callchain_hits, &callchain_param); |
| 1225 | min_callchain_hits); | ||
| 1226 | else if (callchain_mode == GRAPH) | ||
| 1227 | sort_chain_graph(&he->sorted_chain, &he->callchain, | ||
| 1228 | min_callchain_hits); | ||
| 1229 | } | ||
| 1230 | 1241 | ||
| 1231 | while (*p != NULL) { | 1242 | while (*p != NULL) { |
| 1232 | parent = *p; | 1243 | parent = *p; |
| @@ -1249,7 +1260,7 @@ static void output__resort(u64 total_samples) | |||
| 1249 | struct rb_root *tree = &hist; | 1260 | struct rb_root *tree = &hist; |
| 1250 | u64 min_callchain_hits; | 1261 | u64 min_callchain_hits; |
| 1251 | 1262 | ||
| 1252 | min_callchain_hits = total_samples * (callchain_min_percent / 100); | 1263 | min_callchain_hits = total_samples * (callchain_param.min_percent / 100); |
| 1253 | 1264 | ||
| 1254 | if (sort__need_collapse) | 1265 | if (sort__need_collapse) |
| 1255 | tree = &collapse_hists; | 1266 | tree = &collapse_hists; |
| @@ -1829,22 +1840,31 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 1829 | 1840 | ||
| 1830 | /* get the output mode */ | 1841 | /* get the output mode */ |
| 1831 | if (!strncmp(tok, "graph", strlen(arg))) | 1842 | if (!strncmp(tok, "graph", strlen(arg))) |
| 1832 | callchain_mode = GRAPH; | 1843 | callchain_param.mode = CHAIN_GRAPH_ABS; |
| 1833 | 1844 | ||
| 1834 | else if (!strncmp(tok, "flat", strlen(arg))) | 1845 | else if (!strncmp(tok, "flat", strlen(arg))) |
| 1835 | callchain_mode = FLAT; | 1846 | callchain_param.mode = CHAIN_FLAT; |
| 1847 | |||
| 1848 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
| 1849 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
| 1850 | |||
| 1836 | else | 1851 | else |
| 1837 | return -1; | 1852 | return -1; |
| 1838 | 1853 | ||
| 1839 | /* get the min percentage */ | 1854 | /* get the min percentage */ |
| 1840 | tok = strtok(NULL, ","); | 1855 | tok = strtok(NULL, ","); |
| 1841 | if (!tok) | 1856 | if (!tok) |
| 1842 | return 0; | 1857 | goto setup; |
| 1843 | 1858 | ||
| 1844 | callchain_min_percent = strtod(tok, &endptr); | 1859 | callchain_param.min_percent = strtod(tok, &endptr); |
| 1845 | if (tok == endptr) | 1860 | if (tok == endptr) |
| 1846 | return -1; | 1861 | return -1; |
| 1847 | 1862 | ||
| 1863 | setup: | ||
| 1864 | if (register_callchain_param(&callchain_param) < 0) { | ||
| 1865 | fprintf(stderr, "Can't register callchain params\n"); | ||
| 1866 | return -1; | ||
| 1867 | } | ||
| 1848 | return 0; | 1868 | return 0; |
| 1849 | } | 1869 | } |
| 1850 | 1870 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 5d244afb7cdf..9d3c8141b8c1 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -32,13 +32,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 32 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 32 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
| 33 | 33 | ||
| 34 | switch (mode) { | 34 | switch (mode) { |
| 35 | case FLAT: | 35 | case CHAIN_FLAT: |
| 36 | if (rnode->hit < chain->hit) | 36 | if (rnode->hit < chain->hit) |
| 37 | p = &(*p)->rb_left; | 37 | p = &(*p)->rb_left; |
| 38 | else | 38 | else |
| 39 | p = &(*p)->rb_right; | 39 | p = &(*p)->rb_right; |
| 40 | break; | 40 | break; |
| 41 | case GRAPH: | 41 | case CHAIN_GRAPH_ABS: /* Falldown */ |
| 42 | case CHAIN_GRAPH_REL: | ||
| 42 | if (rnode->cumul_hit < chain->cumul_hit) | 43 | if (rnode->cumul_hit < chain->cumul_hit) |
| 43 | p = &(*p)->rb_left; | 44 | p = &(*p)->rb_left; |
| 44 | else | 45 | else |
| @@ -53,43 +54,96 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
| 53 | rb_insert_color(&chain->rb_node, root); | 54 | rb_insert_color(&chain->rb_node, root); |
| 54 | } | 55 | } |
| 55 | 56 | ||
| 57 | static void | ||
| 58 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | ||
| 59 | u64 min_hit) | ||
| 60 | { | ||
| 61 | struct callchain_node *child; | ||
| 62 | |||
| 63 | chain_for_each_child(child, node) | ||
| 64 | __sort_chain_flat(rb_root, child, min_hit); | ||
| 65 | |||
| 66 | if (node->hit && node->hit >= min_hit) | ||
| 67 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); | ||
| 68 | } | ||
| 69 | |||
| 56 | /* | 70 | /* |
| 57 | * Once we get every callchains from the stream, we can now | 71 | * Once we get every callchains from the stream, we can now |
| 58 | * sort them by hit | 72 | * sort them by hit |
| 59 | */ | 73 | */ |
| 60 | void sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | 74 | static void |
| 61 | u64 min_hit) | 75 | sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, |
| 76 | u64 min_hit, struct callchain_param *param __used) | ||
| 77 | { | ||
| 78 | __sort_chain_flat(rb_root, node, min_hit); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void __sort_chain_graph_abs(struct callchain_node *node, | ||
| 82 | u64 min_hit) | ||
| 62 | { | 83 | { |
| 63 | struct callchain_node *child; | 84 | struct callchain_node *child; |
| 64 | 85 | ||
| 65 | chain_for_each_child(child, node) | 86 | node->rb_root = RB_ROOT; |
| 66 | sort_chain_flat(rb_root, child, min_hit); | ||
| 67 | 87 | ||
| 68 | if (node->hit && node->hit >= min_hit) | 88 | chain_for_each_child(child, node) { |
| 69 | rb_insert_callchain(rb_root, node, FLAT); | 89 | __sort_chain_graph_abs(child, min_hit); |
| 90 | if (child->cumul_hit >= min_hit) | ||
| 91 | rb_insert_callchain(&node->rb_root, child, | ||
| 92 | CHAIN_GRAPH_ABS); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | static void | ||
| 97 | sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root, | ||
| 98 | u64 min_hit, struct callchain_param *param __used) | ||
| 99 | { | ||
| 100 | __sort_chain_graph_abs(chain_root, min_hit); | ||
| 101 | rb_root->rb_node = chain_root->rb_root.rb_node; | ||
| 70 | } | 102 | } |
| 71 | 103 | ||
| 72 | static void __sort_chain_graph(struct callchain_node *node, u64 min_hit) | 104 | static void __sort_chain_graph_rel(struct callchain_node *node, |
| 105 | double min_percent) | ||
| 73 | { | 106 | { |
| 74 | struct callchain_node *child; | 107 | struct callchain_node *child; |
| 108 | u64 min_hit; | ||
| 75 | 109 | ||
| 76 | node->rb_root = RB_ROOT; | 110 | node->rb_root = RB_ROOT; |
| 111 | min_hit = node->cumul_hit * min_percent / 100.0; | ||
| 77 | 112 | ||
| 78 | chain_for_each_child(child, node) { | 113 | chain_for_each_child(child, node) { |
| 79 | __sort_chain_graph(child, min_hit); | 114 | __sort_chain_graph_rel(child, min_percent); |
| 80 | if (child->cumul_hit >= min_hit) | 115 | if (child->cumul_hit >= min_hit) |
| 81 | rb_insert_callchain(&node->rb_root, child, GRAPH); | 116 | rb_insert_callchain(&node->rb_root, child, |
| 117 | CHAIN_GRAPH_REL); | ||
| 82 | } | 118 | } |
| 83 | } | 119 | } |
| 84 | 120 | ||
| 85 | void | 121 | static void |
| 86 | sort_chain_graph(struct rb_root *rb_root, struct callchain_node *chain_root, | 122 | sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root, |
| 87 | u64 min_hit) | 123 | u64 min_hit __used, struct callchain_param *param) |
| 88 | { | 124 | { |
| 89 | __sort_chain_graph(chain_root, min_hit); | 125 | __sort_chain_graph_rel(chain_root, param->min_percent); |
| 90 | rb_root->rb_node = chain_root->rb_root.rb_node; | 126 | rb_root->rb_node = chain_root->rb_root.rb_node; |
| 91 | } | 127 | } |
| 92 | 128 | ||
| 129 | int register_callchain_param(struct callchain_param *param) | ||
| 130 | { | ||
| 131 | switch (param->mode) { | ||
| 132 | case CHAIN_GRAPH_ABS: | ||
| 133 | param->sort = sort_chain_graph_abs; | ||
| 134 | break; | ||
| 135 | case CHAIN_GRAPH_REL: | ||
| 136 | param->sort = sort_chain_graph_rel; | ||
| 137 | break; | ||
| 138 | case CHAIN_FLAT: | ||
| 139 | param->sort = sort_chain_flat; | ||
| 140 | break; | ||
| 141 | default: | ||
| 142 | return -1; | ||
| 143 | } | ||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 93 | /* | 147 | /* |
| 94 | * Create a child for a parent. If inherit_children, then the new child | 148 | * Create a child for a parent. If inherit_children, then the new child |
| 95 | * will become the new parent of it's parent children | 149 | * will become the new parent of it's parent children |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index f3e4776e7430..7812122bea1d 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -7,8 +7,9 @@ | |||
| 7 | #include "symbol.h" | 7 | #include "symbol.h" |
| 8 | 8 | ||
| 9 | enum chain_mode { | 9 | enum chain_mode { |
| 10 | FLAT, | 10 | CHAIN_FLAT, |
| 11 | GRAPH | 11 | CHAIN_GRAPH_ABS, |
| 12 | CHAIN_GRAPH_REL | ||
| 12 | }; | 13 | }; |
| 13 | 14 | ||
| 14 | struct callchain_node { | 15 | struct callchain_node { |
| @@ -23,6 +24,17 @@ struct callchain_node { | |||
| 23 | u64 cumul_hit; /* hit + hits of children */ | 24 | u64 cumul_hit; /* hit + hits of children */ |
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 27 | struct callchain_param; | ||
| 28 | |||
| 29 | typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *, | ||
| 30 | u64, struct callchain_param *); | ||
| 31 | |||
| 32 | struct callchain_param { | ||
| 33 | enum chain_mode mode; | ||
| 34 | double min_percent; | ||
| 35 | sort_chain_func_t sort; | ||
| 36 | }; | ||
| 37 | |||
| 26 | struct callchain_list { | 38 | struct callchain_list { |
| 27 | u64 ip; | 39 | u64 ip; |
| 28 | struct symbol *sym; | 40 | struct symbol *sym; |
| @@ -36,10 +48,7 @@ static inline void callchain_init(struct callchain_node *node) | |||
| 36 | INIT_LIST_HEAD(&node->val); | 48 | INIT_LIST_HEAD(&node->val); |
| 37 | } | 49 | } |
| 38 | 50 | ||
| 51 | int register_callchain_param(struct callchain_param *param); | ||
| 39 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 52 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
| 40 | struct symbol **syms); | 53 | struct symbol **syms); |
| 41 | void sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | ||
| 42 | u64 min_hit); | ||
| 43 | void sort_chain_graph(struct rb_root *rb_root, struct callchain_node *node, | ||
| 44 | u64 min_hit); | ||
| 45 | #endif | 54 | #endif |
