perf annotate-data: Add arch_get_reg_offset helper

Add a helper to compute the effective offset from a base register
for instruction operands. Currently handles arm64 addressing modes
including post-indexed, single-register, SIB-style indexed with
shift/extension, and constant-offset register pairs.

Fallback to instruction's raw offset for other architectures.

Reviewed-by: Ian Rogers <irogers@google.com>
Reviewed-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Tengda Wu <wutengda@huaweicloud.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Tengda Wu 2026-10-20 13:44:59 +00:00 committed by Arnaldo Carvalho de Melo
parent 059f12f8cd
commit 68cedb761d
2 changed files with 98 additions and 21 deletions

View File

@ -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)) {

View File

@ -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 *