1#include "util/map_symbol.h"
2#include "util/branch.h"
3#include <linux/kernel.h>
4
5static 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
18void 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
42const 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
74const 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
101const 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
112void 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
169static 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
174int 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
216const 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

source code of linux/tools/perf/util/branch.c