1 | #include "util/map_symbol.h" |
2 | #include "util/branch.h" |
3 | #include <linux/kernel.h> |
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 | |
24 | if (flags->type == PERF_BR_EXTEND_ABI) |
25 | st->new_counts[flags->new_type]++; |
26 | else |
27 | st->counts[flags->type]++; |
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(addr1: from, addr2: to, AREA_2M)) |
37 | st->cross_2m++; |
38 | else if (cross_area(addr1: from, addr2: to, AREA_4K)) |
39 | st->cross_4k++; |
40 | } |
41 | |
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" , |
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 |
60 | "ARCH_1" , |
61 | "ARCH_2" , |
62 | "ARCH_3" , |
63 | "ARCH_4" , |
64 | "ARCH_5" |
65 | #endif |
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 | |
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" , |
87 | "COND_RET" , |
88 | "ERET" , |
89 | "IRQ" , |
90 | "SERROR" , |
91 | "NO_TX" , |
92 | "" , // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref |
93 | }; |
94 | |
95 | if (type >= 0 && type < PERF_BR_MAX) |
96 | return branch_names[type]; |
97 | |
98 | return NULL; |
99 | } |
100 | |
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(new_type: e->flags.new_type); |
108 | |
109 | return branch_type_name(type: e->flags.type); |
110 | } |
111 | |
112 | void branch_type_stat_display(FILE *fp, const struct branch_type_stat *st) |
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(type: i), |
155 | 100.0 * |
156 | (double)st->counts[i] / (double)total); |
157 | } |
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(new_type: i), |
163 | 100.0 * |
164 | (double)st->new_counts[i] / (double)total); |
165 | } |
166 | |
167 | } |
168 | |
169 | static int count_str_scnprintf(int idx, const char *str, char *bf, int size) |
170 | { |
171 | return scnprintf(buf: bf, size, fmt: "%s%s" , (idx) ? " " : " (" , str); |
172 | } |
173 | |
174 | int branch_type_str(const struct branch_type_stat *st, char *bf, int size) |
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 | |
182 | for (i = 0; i < PERF_BR_NEW_MAX; i++) |
183 | total += st->new_counts[i]; |
184 | |
185 | if (total == 0) |
186 | return 0; |
187 | |
188 | if (st->cond_fwd > 0) |
189 | printed += count_str_scnprintf(idx: j++, str: "COND_FWD" , bf: bf + printed, size: size - printed); |
190 | |
191 | if (st->cond_bwd > 0) |
192 | printed += count_str_scnprintf(idx: j++, str: "COND_BWD" , bf: bf + printed, size: 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(idx: j++, str: branch_type_name(type: i), bf: bf + printed, size: size - printed); |
200 | } |
201 | |
202 | for (i = 0; i < PERF_BR_NEW_MAX; i++) { |
203 | if (st->new_counts[i] > 0) |
204 | printed += count_str_scnprintf(idx: j++, str: branch_new_type_name(new_type: i), bf: bf + printed, size: size - printed); |
205 | } |
206 | |
207 | if (st->cross_4k > 0) |
208 | printed += count_str_scnprintf(idx: j++, str: "CROSS_4K" , bf: bf + printed, size: size - printed); |
209 | |
210 | if (st->cross_2m > 0) |
211 | printed += count_str_scnprintf(idx: j++, str: "CROSS_2M" , bf: bf + printed, size: size - printed); |
212 | |
213 | return printed; |
214 | } |
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 | } |
230 | |