diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 398795ba3694..24d82739b2ec 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1241,6 +1241,74 @@ static void setup_stack_canary(struct data_loc_info *dloc) } } +int arch_get_reg_offset(const struct arch *arch, struct annotated_op_loc *op_loc, + int breg, struct type_state *state, bool apply_index, + int *offset) +{ + if (arch__is_arm64(arch)) { + int reg2; + + /* Post-indexed addressing: offset is 0 when not applying index */ + if (op_loc->addr_mode == PERF_AAM_POST_INDEX && !apply_index) { + *offset = 0; + return 0; + } + + /* Single-register addressing: return the stored offset directly */ + if (!op_loc->multi_regs) { + *offset = op_loc->offset; + return 0; + } + + /* + * SIB (Scale-Index-Base) style indexed addressing, e.g.: + * Array element load: ldr dst, [base, index, lsl #3] + * Array element load: ldr dst, [base, index, uxtw #3] + * Array address calc: add dst, base, index lsl #3 + * + * The index reg only computes element offset without modifying + * the underlying data type; the dst type is inherited from base. + * Therefore, the base reg offset can be treated as 0. + */ + if (op_loc->extend_type || op_loc->shift_type) { + /* A memory reference implies breg is a pointer. */ + if (op_loc->mem_ref) { + *offset = 0; + return 0; + } + /* + * Otherwise, require breg to be a known pointer to avoid + * treating integer arithmetic as indexed address calculation. + */ + if (has_reg_type(state, breg) && state->regs[breg].ok && + (state->regs[breg].kind == TSR_KIND_POINTER || + (state->regs[breg].kind == TSR_KIND_TYPE && + dwarf_tag(&state->regs[breg].type) == DW_TAG_pointer_type))) { + *offset = 0; + return 0; + } + return -1; + } + + /* + * For register-based addressing without shift/extension, + * reg2 may be a constant offset. Use its value as the offset + * from the base register. + */ + reg2 = op_loc->reg1 == breg ? op_loc->reg2 : op_loc->reg1; + if (has_reg_type(state, reg2) && state->regs[reg2].ok && + state->regs[reg2].kind == TSR_KIND_CONST) { + *offset = (s64)state->regs[reg2].imm_value; + return 0; + } + + return -1; + } + + *offset = op_loc->offset; + return 0; +} + /* * It's at the target address, check if it has a matching type. * It returns PERF_TMR_BAIL_OUT when it looks up per-cpu variables which @@ -1254,18 +1322,25 @@ static enum type_match_result check_matching_type(struct type_state *state, { u32 insn_offset = dl->al.offset; int reg = dloc->op->reg1; - int offset = dloc->op->offset; - const char *offset_sign = ""; + int offset; + const char *offset_sign; bool retry = true; - if (offset < 0) { - offset = -offset; - offset_sign = "-"; +again: + if (arch_get_reg_offset(dloc->arch, dloc->op, reg, state, false, &offset)) { + /* + * Fall back to the instruction's offset. This prevents the + * register type of the instruction from being completely lost. + * Compared to dropping this type inference entirely, providing + * a rough type hint is likely more useful. + */ + offset = dloc->op->offset; } -again: + offset_sign = offset < 0 ? "-" : ""; + pr_debug_dtp("chk [%x] reg%d offset=%s%#x ok=%d kind=%d ", - insn_offset, reg, offset_sign, offset, + insn_offset, reg, offset_sign, abs(offset), state->regs[reg].ok, state->regs[reg].kind); if (!state->regs[reg].ok) @@ -1288,7 +1363,7 @@ static enum type_match_result check_matching_type(struct type_state *state, */ ptr_type = die_get_pointer_type(&state->regs[reg].type, &ptr_die); if (!ptr_type) { - if (dloc->op->offset < 0 && reg != state->stack_reg) + if (offset < 0 && reg != state->stack_reg) goto check_kernel; return PERF_TMR_NO_POINTER; @@ -1298,7 +1373,7 @@ static enum type_match_result check_matching_type(struct type_state *state, if (__die_get_real_type(ptr_type, type_die) == NULL) return PERF_TMR_NO_POINTER; - dloc->type_offset = dloc->op->offset + state->regs[reg].offset; + dloc->type_offset = offset + state->regs[reg].offset; if (dwarf_tag(type_die) == DW_TAG_typedef) die_get_real_type(type_die, &sized_type); @@ -1322,7 +1397,7 @@ static enum type_match_result check_matching_type(struct type_state *state, */ *type_die = state->regs[reg].type; - dloc->type_offset = dloc->op->offset + state->regs[reg].offset; + dloc->type_offset = offset + state->regs[reg].offset; return check_type_offset(type_die, dloc->type_offset); } @@ -1336,7 +1411,7 @@ static enum type_match_result check_matching_type(struct type_state *state, */ *type_die = state->regs[reg].type; - dloc->type_offset = dloc->op->offset; + dloc->type_offset = offset; if (check_type_offset(type_die, dloc->type_offset) != PERF_TMR_OK) return PERF_TMR_BAIL_OUT; @@ -1358,7 +1433,7 @@ static enum type_match_result check_matching_type(struct type_state *state, } if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) { - u64 var_addr = dloc->op->offset; + u64 var_addr = (s64) offset; int var_offset; pr_debug_dtp("percpu var"); @@ -1385,7 +1460,7 @@ static enum type_match_result check_matching_type(struct type_state *state, if (state->regs[reg].kind == TSR_KIND_CONST && dso__kernel(map__dso(dloc->ms->map))) { - if (dloc->op->offset < 0 && reg != state->stack_reg && reg != dloc->fbreg) + if (offset < 0 && reg != state->stack_reg && reg != dloc->fbreg) goto check_kernel; } check_non_register: @@ -1394,7 +1469,7 @@ static enum type_match_result check_matching_type(struct type_state *state, pr_debug_dtp("%s", reg == dloc->fbreg ? "fbreg" : "stack"); - stack = find_stack_state(state, dloc->type_offset); + stack = find_stack_state(state, offset); if (stack == NULL) { if (retry) { pr_debug_dtp(" : retry\n"); @@ -1417,7 +1492,7 @@ static enum type_match_result check_matching_type(struct type_state *state, *type_die = stack->type; /* Update the type offset from the start of slot */ - dloc->type_offset -= stack->offset; + dloc->type_offset = offset - stack->offset; return PERF_TMR_OK; } @@ -1435,7 +1510,7 @@ static enum type_match_result check_matching_type(struct type_state *state, if (reg != fbreg) return PERF_TMR_NO_TYPE; - stack = find_stack_state(state, dloc->type_offset - fboff); + stack = find_stack_state(state, offset - fboff); if (stack == NULL) { if (retry) { pr_debug_dtp(" : retry\n"); @@ -1458,7 +1533,7 @@ static enum type_match_result check_matching_type(struct type_state *state, *type_die = stack->type; /* Update the type offset from the start of slot */ - dloc->type_offset -= fboff + stack->offset; + dloc->type_offset = offset - fboff - stack->offset; return PERF_TMR_OK; } @@ -1472,7 +1547,7 @@ static enum type_match_result check_matching_type(struct type_state *state, arch__is_x86(dloc->arch)) { pr_debug_dtp("this-cpu var"); - addr = dloc->op->offset; + addr = (s64) offset; if (get_global_var_type(cu_die, dloc, dloc->ip, addr, &offset, type_die)) { @@ -1483,8 +1558,8 @@ static enum type_match_result check_matching_type(struct type_state *state, } /* Access to global variable like "-0x7dcf0500(,%rdx,8)" */ - if (dloc->op->offset < 0 && reg != state->stack_reg) { - addr = (s64) dloc->op->offset; + if (offset < 0 && reg != state->stack_reg) { + addr = (s64) offset; if (get_global_var_type(cu_die, dloc, dloc->ip, addr, &offset, type_die)) { diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index d0dc056fb13a..453efdf6afa6 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -274,7 +274,9 @@ bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, bool get_global_var_info(struct data_loc_info *dloc, u64 addr, const char **var_name, int *var_offset); void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind); - +int arch_get_reg_offset(const struct arch *arch, struct annotated_op_loc *op_loc, + int breg, struct type_state *state, bool apply_index, + int *offset); #else /* HAVE_LIBDW_SUPPORT */ static inline struct annotated_data_type *