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(from, to, AREA_2M))
37		st->cross_2m++;
38	else if (cross_area(from, 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(e->flags.new_type);
108
109	return branch_type_name(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(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
169static 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
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(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
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