xref: /openbmc/linux/tools/perf/util/branch.c (revision 6ade6c64)
1d3300a3cSArnaldo Carvalho de Melo #include "util/map_symbol.h"
2992c7e92SJin Yao #include "util/branch.h"
38520a98dSArnaldo Carvalho de Melo #include <linux/kernel.h>
4992c7e92SJin Yao 
cross_area(u64 addr1,u64 addr2,int size)5992c7e92SJin Yao static bool cross_area(u64 addr1, u64 addr2, int size)
6992c7e92SJin Yao {
7992c7e92SJin Yao 	u64 align1, align2;
8992c7e92SJin Yao 
9992c7e92SJin Yao 	align1 = addr1 & ~(size - 1);
10992c7e92SJin Yao 	align2 = addr2 & ~(size - 1);
11992c7e92SJin Yao 
12992c7e92SJin Yao 	return (align1 != align2) ? true : false;
13992c7e92SJin Yao }
14992c7e92SJin Yao 
15992c7e92SJin Yao #define AREA_4K		4096
16992c7e92SJin Yao #define AREA_2M		(2 * 1024 * 1024)
17992c7e92SJin Yao 
branch_type_count(struct branch_type_stat * st,struct branch_flags * flags,u64 from,u64 to)18992c7e92SJin Yao void branch_type_count(struct branch_type_stat *st, struct branch_flags *flags,
19992c7e92SJin Yao 		       u64 from, u64 to)
20992c7e92SJin Yao {
21992c7e92SJin Yao 	if (flags->type == PERF_BR_UNKNOWN || from == 0)
22992c7e92SJin Yao 		return;
23992c7e92SJin Yao 
240ddea8e2SAnshuman Khandual 	if (flags->type == PERF_BR_EXTEND_ABI)
250ddea8e2SAnshuman Khandual 		st->new_counts[flags->new_type]++;
260ddea8e2SAnshuman Khandual 	else
27992c7e92SJin Yao 		st->counts[flags->type]++;
28992c7e92SJin Yao 
29992c7e92SJin Yao 	if (flags->type == PERF_BR_COND) {
30992c7e92SJin Yao 		if (to > from)
31992c7e92SJin Yao 			st->cond_fwd++;
32992c7e92SJin Yao 		else
33992c7e92SJin Yao 			st->cond_bwd++;
34992c7e92SJin Yao 	}
35992c7e92SJin Yao 
36992c7e92SJin Yao 	if (cross_area(from, to, AREA_2M))
37992c7e92SJin Yao 		st->cross_2m++;
38992c7e92SJin Yao 	else if (cross_area(from, to, AREA_4K))
39992c7e92SJin Yao 		st->cross_4k++;
40992c7e92SJin Yao }
41992c7e92SJin Yao 
branch_new_type_name(int new_type)420ddea8e2SAnshuman Khandual const char *branch_new_type_name(int new_type)
430ddea8e2SAnshuman Khandual {
440ddea8e2SAnshuman Khandual 	const char *branch_new_names[PERF_BR_NEW_MAX] = {
450ddea8e2SAnshuman Khandual 		"FAULT_ALGN",
460ddea8e2SAnshuman Khandual 		"FAULT_DATA",
470ddea8e2SAnshuman Khandual 		"FAULT_INST",
48fb42f8b7SAnshuman Khandual /*
49fb42f8b7SAnshuman Khandual  * TODO: This switch should happen on 'session->header.env.arch'
50fb42f8b7SAnshuman Khandual  * instead, because an arm64 platform perf recording could be
51fb42f8b7SAnshuman Khandual  * opened for analysis on other platforms as well.
52fb42f8b7SAnshuman Khandual  */
53fb42f8b7SAnshuman Khandual #ifdef __aarch64__
54fb42f8b7SAnshuman Khandual 		"ARM64_FIQ",
55fb42f8b7SAnshuman Khandual 		"ARM64_DEBUG_HALT",
56fb42f8b7SAnshuman Khandual 		"ARM64_DEBUG_EXIT",
57fb42f8b7SAnshuman Khandual 		"ARM64_DEBUG_INST",
58fb42f8b7SAnshuman Khandual 		"ARM64_DEBUG_DATA"
59fb42f8b7SAnshuman Khandual #else
600ddea8e2SAnshuman Khandual 		"ARCH_1",
610ddea8e2SAnshuman Khandual 		"ARCH_2",
620ddea8e2SAnshuman Khandual 		"ARCH_3",
630ddea8e2SAnshuman Khandual 		"ARCH_4",
640ddea8e2SAnshuman Khandual 		"ARCH_5"
65fb42f8b7SAnshuman Khandual #endif
660ddea8e2SAnshuman Khandual 	};
670ddea8e2SAnshuman Khandual 
680ddea8e2SAnshuman Khandual 	if (new_type >= 0 && new_type < PERF_BR_NEW_MAX)
690ddea8e2SAnshuman Khandual 		return branch_new_names[new_type];
700ddea8e2SAnshuman Khandual 
710ddea8e2SAnshuman Khandual 	return NULL;
720ddea8e2SAnshuman Khandual }
730ddea8e2SAnshuman Khandual 
branch_type_name(int type)74992c7e92SJin Yao const char *branch_type_name(int type)
75992c7e92SJin Yao {
76992c7e92SJin Yao 	const char *branch_names[PERF_BR_MAX] = {
77992c7e92SJin Yao 		"N/A",
78992c7e92SJin Yao 		"COND",
79992c7e92SJin Yao 		"UNCOND",
80992c7e92SJin Yao 		"IND",
81992c7e92SJin Yao 		"CALL",
82992c7e92SJin Yao 		"IND_CALL",
83992c7e92SJin Yao 		"RET",
84992c7e92SJin Yao 		"SYSCALL",
85992c7e92SJin Yao 		"SYSRET",
86992c7e92SJin Yao 		"COND_CALL",
87cedd3614SAnshuman Khandual 		"COND_RET",
88cedd3614SAnshuman Khandual 		"ERET",
891c96b6e4SAnshuman Khandual 		"IRQ",
901c96b6e4SAnshuman Khandual 		"SERROR",
910ddea8e2SAnshuman Khandual 		"NO_TX",
920ddea8e2SAnshuman Khandual 		"", // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref
93992c7e92SJin Yao 	};
94992c7e92SJin Yao 
95992c7e92SJin Yao 	if (type >= 0 && type < PERF_BR_MAX)
96992c7e92SJin Yao 		return branch_names[type];
97992c7e92SJin Yao 
98992c7e92SJin Yao 	return NULL;
99992c7e92SJin Yao }
100992c7e92SJin Yao 
get_branch_type(struct branch_entry * e)1010ddea8e2SAnshuman Khandual const char *get_branch_type(struct branch_entry *e)
1020ddea8e2SAnshuman Khandual {
1030ddea8e2SAnshuman Khandual 	if (e->flags.type == PERF_BR_UNKNOWN)
1040ddea8e2SAnshuman Khandual 		return "";
1050ddea8e2SAnshuman Khandual 
1060ddea8e2SAnshuman Khandual 	if (e->flags.type == PERF_BR_EXTEND_ABI)
1070ddea8e2SAnshuman Khandual 		return branch_new_type_name(e->flags.new_type);
1080ddea8e2SAnshuman Khandual 
1090ddea8e2SAnshuman Khandual 	return branch_type_name(e->flags.type);
1100ddea8e2SAnshuman Khandual }
1110ddea8e2SAnshuman Khandual 
branch_type_stat_display(FILE * fp,struct branch_type_stat * st)112992c7e92SJin Yao void branch_type_stat_display(FILE *fp, struct branch_type_stat *st)
113992c7e92SJin Yao {
114992c7e92SJin Yao 	u64 total = 0;
115992c7e92SJin Yao 	int i;
116992c7e92SJin Yao 
117992c7e92SJin Yao 	for (i = 0; i < PERF_BR_MAX; i++)
118992c7e92SJin Yao 		total += st->counts[i];
119992c7e92SJin Yao 
120992c7e92SJin Yao 	if (total == 0)
121992c7e92SJin Yao 		return;
122992c7e92SJin Yao 
123992c7e92SJin Yao 	fprintf(fp, "\n#");
124992c7e92SJin Yao 	fprintf(fp, "\n# Branch Statistics:");
125992c7e92SJin Yao 	fprintf(fp, "\n#");
126992c7e92SJin Yao 
127992c7e92SJin Yao 	if (st->cond_fwd > 0) {
128992c7e92SJin Yao 		fprintf(fp, "\n%8s: %5.1f%%",
129992c7e92SJin Yao 			"COND_FWD",
130992c7e92SJin Yao 			100.0 * (double)st->cond_fwd / (double)total);
131992c7e92SJin Yao 	}
132992c7e92SJin Yao 
133992c7e92SJin Yao 	if (st->cond_bwd > 0) {
134992c7e92SJin Yao 		fprintf(fp, "\n%8s: %5.1f%%",
135992c7e92SJin Yao 			"COND_BWD",
136992c7e92SJin Yao 			100.0 * (double)st->cond_bwd / (double)total);
137992c7e92SJin Yao 	}
138992c7e92SJin Yao 
139992c7e92SJin Yao 	if (st->cross_4k > 0) {
140992c7e92SJin Yao 		fprintf(fp, "\n%8s: %5.1f%%",
141992c7e92SJin Yao 			"CROSS_4K",
142992c7e92SJin Yao 			100.0 * (double)st->cross_4k / (double)total);
143992c7e92SJin Yao 	}
144992c7e92SJin Yao 
145992c7e92SJin Yao 	if (st->cross_2m > 0) {
146992c7e92SJin Yao 		fprintf(fp, "\n%8s: %5.1f%%",
147992c7e92SJin Yao 			"CROSS_2M",
148992c7e92SJin Yao 			100.0 * (double)st->cross_2m / (double)total);
149992c7e92SJin Yao 	}
150992c7e92SJin Yao 
151992c7e92SJin Yao 	for (i = 0; i < PERF_BR_MAX; i++) {
152992c7e92SJin Yao 		if (st->counts[i] > 0)
153992c7e92SJin Yao 			fprintf(fp, "\n%8s: %5.1f%%",
154992c7e92SJin Yao 				branch_type_name(i),
155992c7e92SJin Yao 				100.0 *
156992c7e92SJin Yao 				(double)st->counts[i] / (double)total);
157992c7e92SJin Yao 	}
1580ddea8e2SAnshuman Khandual 
1590ddea8e2SAnshuman Khandual 	for (i = 0; i < PERF_BR_NEW_MAX; i++) {
1600ddea8e2SAnshuman Khandual 		if (st->new_counts[i] > 0)
1610ddea8e2SAnshuman Khandual 			fprintf(fp, "\n%8s: %5.1f%%",
1620ddea8e2SAnshuman Khandual 				branch_new_type_name(i),
1630ddea8e2SAnshuman Khandual 				100.0 *
1640ddea8e2SAnshuman Khandual 				(double)st->new_counts[i] / (double)total);
1650ddea8e2SAnshuman Khandual 	}
1660ddea8e2SAnshuman Khandual 
167992c7e92SJin Yao }
168992c7e92SJin Yao 
count_str_scnprintf(int idx,const char * str,char * bf,int size)169992c7e92SJin Yao static int count_str_scnprintf(int idx, const char *str, char *bf, int size)
170992c7e92SJin Yao {
171992c7e92SJin Yao 	return scnprintf(bf, size, "%s%s", (idx) ? " " : " (", str);
172992c7e92SJin Yao }
173992c7e92SJin Yao 
branch_type_str(struct branch_type_stat * st,char * bf,int size)174992c7e92SJin Yao int branch_type_str(struct branch_type_stat *st, char *bf, int size)
175992c7e92SJin Yao {
176992c7e92SJin Yao 	int i, j = 0, printed = 0;
177992c7e92SJin Yao 	u64 total = 0;
178992c7e92SJin Yao 
179992c7e92SJin Yao 	for (i = 0; i < PERF_BR_MAX; i++)
180992c7e92SJin Yao 		total += st->counts[i];
181992c7e92SJin Yao 
1820ddea8e2SAnshuman Khandual 	for (i = 0; i < PERF_BR_NEW_MAX; i++)
1830ddea8e2SAnshuman Khandual 		total += st->new_counts[i];
1840ddea8e2SAnshuman Khandual 
185992c7e92SJin Yao 	if (total == 0)
186992c7e92SJin Yao 		return 0;
187992c7e92SJin Yao 
188992c7e92SJin Yao 	if (st->cond_fwd > 0)
189992c7e92SJin Yao 		printed += count_str_scnprintf(j++, "COND_FWD", bf + printed, size - printed);
190992c7e92SJin Yao 
191992c7e92SJin Yao 	if (st->cond_bwd > 0)
192992c7e92SJin Yao 		printed += count_str_scnprintf(j++, "COND_BWD", bf + printed, size - printed);
193992c7e92SJin Yao 
194992c7e92SJin Yao 	for (i = 0; i < PERF_BR_MAX; i++) {
195992c7e92SJin Yao 		if (i == PERF_BR_COND)
196992c7e92SJin Yao 			continue;
197992c7e92SJin Yao 
198992c7e92SJin Yao 		if (st->counts[i] > 0)
199992c7e92SJin Yao 			printed += count_str_scnprintf(j++, branch_type_name(i), bf + printed, size - printed);
200992c7e92SJin Yao 	}
201992c7e92SJin Yao 
2020ddea8e2SAnshuman Khandual 	for (i = 0; i < PERF_BR_NEW_MAX; i++) {
2030ddea8e2SAnshuman Khandual 		if (st->new_counts[i] > 0)
2040ddea8e2SAnshuman Khandual 			printed += count_str_scnprintf(j++, branch_new_type_name(i), bf + printed, size - printed);
2050ddea8e2SAnshuman Khandual 	}
2060ddea8e2SAnshuman Khandual 
207992c7e92SJin Yao 	if (st->cross_4k > 0)
208992c7e92SJin Yao 		printed += count_str_scnprintf(j++, "CROSS_4K", bf + printed, size - printed);
209992c7e92SJin Yao 
210992c7e92SJin Yao 	if (st->cross_2m > 0)
211992c7e92SJin Yao 		printed += count_str_scnprintf(j++, "CROSS_2M", bf + printed, size - printed);
212992c7e92SJin Yao 
213992c7e92SJin Yao 	return printed;
214992c7e92SJin Yao }
215*6ade6c64SSandipan Das 
branch_spec_desc(int spec)216*6ade6c64SSandipan Das const char *branch_spec_desc(int spec)
217*6ade6c64SSandipan Das {
218*6ade6c64SSandipan Das 	const char *branch_spec_outcomes[PERF_BR_SPEC_MAX] = {
219*6ade6c64SSandipan Das 		"N/A",
220*6ade6c64SSandipan Das 		"SPEC_WRONG_PATH",
221*6ade6c64SSandipan Das 		"NON_SPEC_CORRECT_PATH",
222*6ade6c64SSandipan Das 		"SPEC_CORRECT_PATH",
223*6ade6c64SSandipan Das 	};
224*6ade6c64SSandipan Das 
225*6ade6c64SSandipan Das 	if (spec >= 0 && spec < PERF_BR_SPEC_MAX)
226*6ade6c64SSandipan Das 		return branch_spec_outcomes[spec];
227*6ade6c64SSandipan Das 
228*6ade6c64SSandipan Das 	return NULL;
229*6ade6c64SSandipan Das }
230