Commit | Line | Data |
---|---|---|
d3300a3c | 1 | #include "util/map_symbol.h" |
992c7e92 | 2 | #include "util/branch.h" |
8520a98d | 3 | #include <linux/kernel.h> |
992c7e92 JY |
4 | |
5 | static bool cross_area(u64 addr1, u64 addr2, int size) | |
6 | { | |
7 | u64 align1, align2; | |
8 | ||
9 | align1 = addr1 & ~(size - 1); | |
10 | align2 = addr2 & ~(size - 1); | |
11 | ||
12 | return (align1 != align2) ? true : false; | |
13 | } | |
14 | ||
15 | #define AREA_4K 4096 | |
16 | #define AREA_2M (2 * 1024 * 1024) | |
17 | ||
18 | void branch_type_count(struct branch_type_stat *st, struct branch_flags *flags, | |
19 | u64 from, u64 to) | |
20 | { | |
21 | if (flags->type == PERF_BR_UNKNOWN || from == 0) | |
22 | return; | |
23 | ||
0ddea8e2 AK |
24 | if (flags->type == PERF_BR_EXTEND_ABI) |
25 | st->new_counts[flags->new_type]++; | |
26 | else | |
27 | st->counts[flags->type]++; | |
992c7e92 JY |
28 | |
29 | if (flags->type == PERF_BR_COND) { | |
30 | if (to > from) | |
31 | st->cond_fwd++; | |
32 | else | |
33 | st->cond_bwd++; | |
34 | } | |
35 | ||
36 | if (cross_area(from, to, AREA_2M)) | |
37 | st->cross_2m++; | |
38 | else if (cross_area(from, to, AREA_4K)) | |
39 | st->cross_4k++; | |
40 | } | |
41 | ||
0ddea8e2 AK |
42 | const char *branch_new_type_name(int new_type) |
43 | { | |
44 | const char *branch_new_names[PERF_BR_NEW_MAX] = { | |
45 | "FAULT_ALGN", | |
46 | "FAULT_DATA", | |
47 | "FAULT_INST", | |
fb42f8b7 AK |
48 | /* |
49 | * TODO: This switch should happen on 'session->header.env.arch' | |
50 | * instead, because an arm64 platform perf recording could be | |
51 | * opened for analysis on other platforms as well. | |
52 | */ | |
53 | #ifdef __aarch64__ | |
54 | "ARM64_FIQ", | |
55 | "ARM64_DEBUG_HALT", | |
56 | "ARM64_DEBUG_EXIT", | |
57 | "ARM64_DEBUG_INST", | |
58 | "ARM64_DEBUG_DATA" | |
59 | #else | |
0ddea8e2 AK |
60 | "ARCH_1", |
61 | "ARCH_2", | |
62 | "ARCH_3", | |
63 | "ARCH_4", | |
64 | "ARCH_5" | |
fb42f8b7 | 65 | #endif |
0ddea8e2 AK |
66 | }; |
67 | ||
68 | if (new_type >= 0 && new_type < PERF_BR_NEW_MAX) | |
69 | return branch_new_names[new_type]; | |
70 | ||
71 | return NULL; | |
72 | } | |
73 | ||
992c7e92 JY |
74 | const char *branch_type_name(int type) |
75 | { | |
76 | const char *branch_names[PERF_BR_MAX] = { | |
77 | "N/A", | |
78 | "COND", | |
79 | "UNCOND", | |
80 | "IND", | |
81 | "CALL", | |
82 | "IND_CALL", | |
83 | "RET", | |
84 | "SYSCALL", | |
85 | "SYSRET", | |
86 | "COND_CALL", | |
cedd3614 AK |
87 | "COND_RET", |
88 | "ERET", | |
1c96b6e4 AK |
89 | "IRQ", |
90 | "SERROR", | |
0ddea8e2 AK |
91 | "NO_TX", |
92 | "", // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref | |
992c7e92 JY |
93 | }; |
94 | ||
95 | if (type >= 0 && type < PERF_BR_MAX) | |
96 | return branch_names[type]; | |
97 | ||
98 | return NULL; | |
99 | } | |
100 | ||
0ddea8e2 AK |
101 | const char *get_branch_type(struct branch_entry *e) |
102 | { | |
103 | if (e->flags.type == PERF_BR_UNKNOWN) | |
104 | return ""; | |
105 | ||
106 | if (e->flags.type == PERF_BR_EXTEND_ABI) | |
107 | return branch_new_type_name(e->flags.new_type); | |
108 | ||
109 | return branch_type_name(e->flags.type); | |
110 | } | |
111 | ||
d47d876d | 112 | void branch_type_stat_display(FILE *fp, const struct branch_type_stat *st) |
992c7e92 JY |
113 | { |
114 | u64 total = 0; | |
115 | int i; | |
116 | ||
117 | for (i = 0; i < PERF_BR_MAX; i++) | |
118 | total += st->counts[i]; | |
119 | ||
120 | if (total == 0) | |
121 | return; | |
122 | ||
123 | fprintf(fp, "\n#"); | |
124 | fprintf(fp, "\n# Branch Statistics:"); | |
125 | fprintf(fp, "\n#"); | |
126 | ||
127 | if (st->cond_fwd > 0) { | |
128 | fprintf(fp, "\n%8s: %5.1f%%", | |
129 | "COND_FWD", | |
130 | 100.0 * (double)st->cond_fwd / (double)total); | |
131 | } | |
132 | ||
133 | if (st->cond_bwd > 0) { | |
134 | fprintf(fp, "\n%8s: %5.1f%%", | |
135 | "COND_BWD", | |
136 | 100.0 * (double)st->cond_bwd / (double)total); | |
137 | } | |
138 | ||
139 | if (st->cross_4k > 0) { | |
140 | fprintf(fp, "\n%8s: %5.1f%%", | |
141 | "CROSS_4K", | |
142 | 100.0 * (double)st->cross_4k / (double)total); | |
143 | } | |
144 | ||
145 | if (st->cross_2m > 0) { | |
146 | fprintf(fp, "\n%8s: %5.1f%%", | |
147 | "CROSS_2M", | |
148 | 100.0 * (double)st->cross_2m / (double)total); | |
149 | } | |
150 | ||
151 | for (i = 0; i < PERF_BR_MAX; i++) { | |
152 | if (st->counts[i] > 0) | |
153 | fprintf(fp, "\n%8s: %5.1f%%", | |
154 | branch_type_name(i), | |
155 | 100.0 * | |
156 | (double)st->counts[i] / (double)total); | |
157 | } | |
0ddea8e2 AK |
158 | |
159 | for (i = 0; i < PERF_BR_NEW_MAX; i++) { | |
160 | if (st->new_counts[i] > 0) | |
161 | fprintf(fp, "\n%8s: %5.1f%%", | |
162 | branch_new_type_name(i), | |
163 | 100.0 * | |
164 | (double)st->new_counts[i] / (double)total); | |
165 | } | |
166 | ||
992c7e92 JY |
167 | } |
168 | ||
169 | static int count_str_scnprintf(int idx, const char *str, char *bf, int size) | |
170 | { | |
171 | return scnprintf(bf, size, "%s%s", (idx) ? " " : " (", str); | |
172 | } | |
173 | ||
d47d876d | 174 | int branch_type_str(const struct branch_type_stat *st, char *bf, int size) |
992c7e92 JY |
175 | { |
176 | int i, j = 0, printed = 0; | |
177 | u64 total = 0; | |
178 | ||
179 | for (i = 0; i < PERF_BR_MAX; i++) | |
180 | total += st->counts[i]; | |
181 | ||
0ddea8e2 AK |
182 | for (i = 0; i < PERF_BR_NEW_MAX; i++) |
183 | total += st->new_counts[i]; | |
184 | ||
992c7e92 JY |
185 | if (total == 0) |
186 | return 0; | |
187 | ||
188 | if (st->cond_fwd > 0) | |
189 | printed += count_str_scnprintf(j++, "COND_FWD", bf + printed, size - printed); | |
190 | ||
191 | if (st->cond_bwd > 0) | |
192 | printed += count_str_scnprintf(j++, "COND_BWD", bf + printed, size - printed); | |
193 | ||
194 | for (i = 0; i < PERF_BR_MAX; i++) { | |
195 | if (i == PERF_BR_COND) | |
196 | continue; | |
197 | ||
198 | if (st->counts[i] > 0) | |
199 | printed += count_str_scnprintf(j++, branch_type_name(i), bf + printed, size - printed); | |
200 | } | |
201 | ||
0ddea8e2 AK |
202 | for (i = 0; i < PERF_BR_NEW_MAX; i++) { |
203 | if (st->new_counts[i] > 0) | |
204 | printed += count_str_scnprintf(j++, branch_new_type_name(i), bf + printed, size - printed); | |
205 | } | |
206 | ||
992c7e92 JY |
207 | if (st->cross_4k > 0) |
208 | printed += count_str_scnprintf(j++, "CROSS_4K", bf + printed, size - printed); | |
209 | ||
210 | if (st->cross_2m > 0) | |
211 | printed += count_str_scnprintf(j++, "CROSS_2M", bf + printed, size - printed); | |
212 | ||
213 | return printed; | |
214 | } | |
6ade6c64 SD |
215 | |
216 | const char *branch_spec_desc(int spec) | |
217 | { | |
218 | const char *branch_spec_outcomes[PERF_BR_SPEC_MAX] = { | |
219 | "N/A", | |
220 | "SPEC_WRONG_PATH", | |
221 | "NON_SPEC_CORRECT_PATH", | |
222 | "SPEC_CORRECT_PATH", | |
223 | }; | |
224 | ||
225 | if (spec >= 0 && spec < PERF_BR_SPEC_MAX) | |
226 | return branch_spec_outcomes[spec]; | |
227 | ||
228 | return NULL; | |
229 | } |