162306a36Sopenharmony_ci#include "util/map_symbol.h"
262306a36Sopenharmony_ci#include "util/branch.h"
362306a36Sopenharmony_ci#include <linux/kernel.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_cistatic bool cross_area(u64 addr1, u64 addr2, int size)
662306a36Sopenharmony_ci{
762306a36Sopenharmony_ci	u64 align1, align2;
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci	align1 = addr1 & ~(size - 1);
1062306a36Sopenharmony_ci	align2 = addr2 & ~(size - 1);
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci	return (align1 != align2) ? true : false;
1362306a36Sopenharmony_ci}
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define AREA_4K		4096
1662306a36Sopenharmony_ci#define AREA_2M		(2 * 1024 * 1024)
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_civoid branch_type_count(struct branch_type_stat *st, struct branch_flags *flags,
1962306a36Sopenharmony_ci		       u64 from, u64 to)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	if (flags->type == PERF_BR_UNKNOWN || from == 0)
2262306a36Sopenharmony_ci		return;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (flags->type == PERF_BR_EXTEND_ABI)
2562306a36Sopenharmony_ci		st->new_counts[flags->new_type]++;
2662306a36Sopenharmony_ci	else
2762306a36Sopenharmony_ci		st->counts[flags->type]++;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (flags->type == PERF_BR_COND) {
3062306a36Sopenharmony_ci		if (to > from)
3162306a36Sopenharmony_ci			st->cond_fwd++;
3262306a36Sopenharmony_ci		else
3362306a36Sopenharmony_ci			st->cond_bwd++;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (cross_area(from, to, AREA_2M))
3762306a36Sopenharmony_ci		st->cross_2m++;
3862306a36Sopenharmony_ci	else if (cross_area(from, to, AREA_4K))
3962306a36Sopenharmony_ci		st->cross_4k++;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciconst char *branch_new_type_name(int new_type)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	const char *branch_new_names[PERF_BR_NEW_MAX] = {
4562306a36Sopenharmony_ci		"FAULT_ALGN",
4662306a36Sopenharmony_ci		"FAULT_DATA",
4762306a36Sopenharmony_ci		"FAULT_INST",
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * TODO: This switch should happen on 'session->header.env.arch'
5062306a36Sopenharmony_ci * instead, because an arm64 platform perf recording could be
5162306a36Sopenharmony_ci * opened for analysis on other platforms as well.
5262306a36Sopenharmony_ci */
5362306a36Sopenharmony_ci#ifdef __aarch64__
5462306a36Sopenharmony_ci		"ARM64_FIQ",
5562306a36Sopenharmony_ci		"ARM64_DEBUG_HALT",
5662306a36Sopenharmony_ci		"ARM64_DEBUG_EXIT",
5762306a36Sopenharmony_ci		"ARM64_DEBUG_INST",
5862306a36Sopenharmony_ci		"ARM64_DEBUG_DATA"
5962306a36Sopenharmony_ci#else
6062306a36Sopenharmony_ci		"ARCH_1",
6162306a36Sopenharmony_ci		"ARCH_2",
6262306a36Sopenharmony_ci		"ARCH_3",
6362306a36Sopenharmony_ci		"ARCH_4",
6462306a36Sopenharmony_ci		"ARCH_5"
6562306a36Sopenharmony_ci#endif
6662306a36Sopenharmony_ci	};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (new_type >= 0 && new_type < PERF_BR_NEW_MAX)
6962306a36Sopenharmony_ci		return branch_new_names[new_type];
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return NULL;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciconst char *branch_type_name(int type)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	const char *branch_names[PERF_BR_MAX] = {
7762306a36Sopenharmony_ci		"N/A",
7862306a36Sopenharmony_ci		"COND",
7962306a36Sopenharmony_ci		"UNCOND",
8062306a36Sopenharmony_ci		"IND",
8162306a36Sopenharmony_ci		"CALL",
8262306a36Sopenharmony_ci		"IND_CALL",
8362306a36Sopenharmony_ci		"RET",
8462306a36Sopenharmony_ci		"SYSCALL",
8562306a36Sopenharmony_ci		"SYSRET",
8662306a36Sopenharmony_ci		"COND_CALL",
8762306a36Sopenharmony_ci		"COND_RET",
8862306a36Sopenharmony_ci		"ERET",
8962306a36Sopenharmony_ci		"IRQ",
9062306a36Sopenharmony_ci		"SERROR",
9162306a36Sopenharmony_ci		"NO_TX",
9262306a36Sopenharmony_ci		"", // Needed for PERF_BR_EXTEND_ABI that ends up triggering some compiler warnings about NULL deref
9362306a36Sopenharmony_ci	};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (type >= 0 && type < PERF_BR_MAX)
9662306a36Sopenharmony_ci		return branch_names[type];
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return NULL;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciconst char *get_branch_type(struct branch_entry *e)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	if (e->flags.type == PERF_BR_UNKNOWN)
10462306a36Sopenharmony_ci		return "";
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (e->flags.type == PERF_BR_EXTEND_ABI)
10762306a36Sopenharmony_ci		return branch_new_type_name(e->flags.new_type);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return branch_type_name(e->flags.type);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid branch_type_stat_display(FILE *fp, struct branch_type_stat *st)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u64 total = 0;
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_MAX; i++)
11862306a36Sopenharmony_ci		total += st->counts[i];
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (total == 0)
12162306a36Sopenharmony_ci		return;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	fprintf(fp, "\n#");
12462306a36Sopenharmony_ci	fprintf(fp, "\n# Branch Statistics:");
12562306a36Sopenharmony_ci	fprintf(fp, "\n#");
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (st->cond_fwd > 0) {
12862306a36Sopenharmony_ci		fprintf(fp, "\n%8s: %5.1f%%",
12962306a36Sopenharmony_ci			"COND_FWD",
13062306a36Sopenharmony_ci			100.0 * (double)st->cond_fwd / (double)total);
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (st->cond_bwd > 0) {
13462306a36Sopenharmony_ci		fprintf(fp, "\n%8s: %5.1f%%",
13562306a36Sopenharmony_ci			"COND_BWD",
13662306a36Sopenharmony_ci			100.0 * (double)st->cond_bwd / (double)total);
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (st->cross_4k > 0) {
14062306a36Sopenharmony_ci		fprintf(fp, "\n%8s: %5.1f%%",
14162306a36Sopenharmony_ci			"CROSS_4K",
14262306a36Sopenharmony_ci			100.0 * (double)st->cross_4k / (double)total);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (st->cross_2m > 0) {
14662306a36Sopenharmony_ci		fprintf(fp, "\n%8s: %5.1f%%",
14762306a36Sopenharmony_ci			"CROSS_2M",
14862306a36Sopenharmony_ci			100.0 * (double)st->cross_2m / (double)total);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_MAX; i++) {
15262306a36Sopenharmony_ci		if (st->counts[i] > 0)
15362306a36Sopenharmony_ci			fprintf(fp, "\n%8s: %5.1f%%",
15462306a36Sopenharmony_ci				branch_type_name(i),
15562306a36Sopenharmony_ci				100.0 *
15662306a36Sopenharmony_ci				(double)st->counts[i] / (double)total);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_NEW_MAX; i++) {
16062306a36Sopenharmony_ci		if (st->new_counts[i] > 0)
16162306a36Sopenharmony_ci			fprintf(fp, "\n%8s: %5.1f%%",
16262306a36Sopenharmony_ci				branch_new_type_name(i),
16362306a36Sopenharmony_ci				100.0 *
16462306a36Sopenharmony_ci				(double)st->new_counts[i] / (double)total);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int count_str_scnprintf(int idx, const char *str, char *bf, int size)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	return scnprintf(bf, size, "%s%s", (idx) ? " " : " (", str);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ciint branch_type_str(struct branch_type_stat *st, char *bf, int size)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	int i, j = 0, printed = 0;
17762306a36Sopenharmony_ci	u64 total = 0;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_MAX; i++)
18062306a36Sopenharmony_ci		total += st->counts[i];
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_NEW_MAX; i++)
18362306a36Sopenharmony_ci		total += st->new_counts[i];
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (total == 0)
18662306a36Sopenharmony_ci		return 0;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (st->cond_fwd > 0)
18962306a36Sopenharmony_ci		printed += count_str_scnprintf(j++, "COND_FWD", bf + printed, size - printed);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (st->cond_bwd > 0)
19262306a36Sopenharmony_ci		printed += count_str_scnprintf(j++, "COND_BWD", bf + printed, size - printed);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_MAX; i++) {
19562306a36Sopenharmony_ci		if (i == PERF_BR_COND)
19662306a36Sopenharmony_ci			continue;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		if (st->counts[i] > 0)
19962306a36Sopenharmony_ci			printed += count_str_scnprintf(j++, branch_type_name(i), bf + printed, size - printed);
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	for (i = 0; i < PERF_BR_NEW_MAX; i++) {
20362306a36Sopenharmony_ci		if (st->new_counts[i] > 0)
20462306a36Sopenharmony_ci			printed += count_str_scnprintf(j++, branch_new_type_name(i), bf + printed, size - printed);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (st->cross_4k > 0)
20862306a36Sopenharmony_ci		printed += count_str_scnprintf(j++, "CROSS_4K", bf + printed, size - printed);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (st->cross_2m > 0)
21162306a36Sopenharmony_ci		printed += count_str_scnprintf(j++, "CROSS_2M", bf + printed, size - printed);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return printed;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ciconst char *branch_spec_desc(int spec)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	const char *branch_spec_outcomes[PERF_BR_SPEC_MAX] = {
21962306a36Sopenharmony_ci		"N/A",
22062306a36Sopenharmony_ci		"SPEC_WRONG_PATH",
22162306a36Sopenharmony_ci		"NON_SPEC_CORRECT_PATH",
22262306a36Sopenharmony_ci		"SPEC_CORRECT_PATH",
22362306a36Sopenharmony_ci	};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (spec >= 0 && spec < PERF_BR_SPEC_MAX)
22662306a36Sopenharmony_ci		return branch_spec_outcomes[spec];
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return NULL;
22962306a36Sopenharmony_ci}
230