xref: /linux/tools/perf/util/branch.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
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(from, to, AREA_2M))
37 		st->cross_2m++;
38 	else if (cross_area(from, 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(e->flags.new_type);
108 
109 	return branch_type_name(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(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(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(bf, size, "%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(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 
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 
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 }
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