mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
059f12f8cd
commit
68cedb761d
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user