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