mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
perf annotate-arm64: Support stack variable tracking
Extend update_insn_state_arm64() to track data types stored on the stack. The implementation handles: 1. Stack Loads: Identify when a register is loaded from a stack slot and update the register's type state based on the tracked stack content or compound member types. 2. Stack Stores: Update or create new stack state entries when a tracked register type is stored to the stack. Similar to loads, stores also support saving one or two registers (in the case of 'stp'). Therefore, propagate_store_reg_state() is introduced. With these changes, the instruction tracker can now follow data types as they move between registers and memory, specifically for function local variables and compiler-spilled values on arm64. 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
aa2cd2e19c
commit
861ecd5c6c
|
|
@ -624,6 +624,8 @@ static int propagate_load_reg_state(struct type_state *state,
|
|||
u32 insn_offset = dl->al.offset;
|
||||
int sreg = src->reg1;
|
||||
int reg_offset;
|
||||
int fbreg = dloc->fbreg;
|
||||
int fboff = 0;
|
||||
|
||||
if (!has_reg_type(state, dreg))
|
||||
return -1;
|
||||
|
|
@ -633,12 +635,57 @@ static int propagate_load_reg_state(struct type_state *state,
|
|||
tsr->lifetime_active = false;
|
||||
tsr->lifetime_end = 0;
|
||||
|
||||
if (dloc->fb_cfa) {
|
||||
u64 ip = dloc->ms->sym->start + dl->al.offset;
|
||||
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
|
||||
|
||||
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
|
||||
fbreg = -1;
|
||||
}
|
||||
|
||||
retry:
|
||||
if (arch_get_reg_offset(dloc->arch, src, sreg, state, false, ®_offset))
|
||||
goto load_non_regoff;
|
||||
|
||||
reg_offset += mem_spacing;
|
||||
|
||||
/* Check stack variables with offset */
|
||||
if (sreg != -1 && (sreg == fbreg || sreg == state->stack_reg)) {
|
||||
struct type_state_stack *stack;
|
||||
int offset = sreg == fbreg ? reg_offset - fboff : reg_offset;
|
||||
|
||||
stack = find_stack_state(state, offset);
|
||||
if (stack == NULL) {
|
||||
return -1;
|
||||
} else if (!stack->compound) {
|
||||
tsr->type = stack->type;
|
||||
tsr->kind = stack->kind;
|
||||
tsr->offset = stack->ptr_offset;
|
||||
tsr->imm_value = stack->imm_value;
|
||||
tsr->ok = true;
|
||||
} else if (die_get_member_type(&stack->type,
|
||||
offset - stack->offset,
|
||||
&type_die)) {
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->offset = 0;
|
||||
tsr->imm_value = 0;
|
||||
tsr->ok = true;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sreg == fbreg) {
|
||||
pr_debug_dtp("%s [%x] -%#x(stack) -> reg%d",
|
||||
dl->ins.name, insn_offset, -offset, dreg);
|
||||
} else {
|
||||
pr_debug_dtp("%s [%x] %#x(reg%d) -> reg%d",
|
||||
dl->ins.name, insn_offset, offset, sreg, dreg);
|
||||
}
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!src_tsr || !src_tsr->ok)
|
||||
return -1;
|
||||
|
||||
|
|
@ -757,18 +804,108 @@ static bool is_standard_store_insn(const char *name)
|
|||
!strncmp(name, "stp", 3); /* stp */
|
||||
}
|
||||
|
||||
/*
|
||||
* For store insns: propagate type from @sreg to the memory location referenced
|
||||
* by @dst, applying @mem_spacing to account for multi-regs stores (e.g. stp).
|
||||
*/
|
||||
static void propagate_store_reg_state(struct type_state *state,
|
||||
struct data_loc_info *dloc,
|
||||
struct disasm_line *dl, int sreg,
|
||||
struct annotated_op_loc *dst,
|
||||
int mem_spacing)
|
||||
{
|
||||
struct type_state_reg *tsr;
|
||||
u32 insn_offset = dl->al.offset;
|
||||
int dreg = dst->reg1;
|
||||
int reg_offset;
|
||||
int fbreg = dloc->fbreg;
|
||||
int fboff = 0;
|
||||
|
||||
if (arch_get_reg_offset(dloc->arch, dst, dreg, state, false, ®_offset))
|
||||
return;
|
||||
|
||||
reg_offset += mem_spacing;
|
||||
|
||||
if (dloc->fb_cfa) {
|
||||
u64 ip = dloc->ms->sym->start + dl->al.offset;
|
||||
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
|
||||
|
||||
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
|
||||
fbreg = -1;
|
||||
}
|
||||
|
||||
/* Check stack variables with offset */
|
||||
if (dreg != -1 && (dreg == fbreg || dreg == state->stack_reg)) {
|
||||
struct type_state_stack *stack;
|
||||
int offset = dreg == fbreg ? reg_offset - fboff : reg_offset;
|
||||
|
||||
if (!has_reg_type(state, sreg) || !state->regs[sreg].ok) {
|
||||
stack = find_stack_state(state, offset);
|
||||
/* Preserve compound states when only a member is overwritten. */
|
||||
if (stack && !stack->compound)
|
||||
delete_stack_state(state, offset);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tsr = &state->regs[sreg];
|
||||
|
||||
stack = find_stack_state(state, offset);
|
||||
if (stack) {
|
||||
if (!stack->compound)
|
||||
set_stack_state(stack, offset, tsr->kind, &tsr->type,
|
||||
tsr->offset, tsr->imm_value);
|
||||
/*
|
||||
* If it's a compound type, it means attempting to
|
||||
* write to a member value of the compound type without
|
||||
* changing the compound type itself, so do nothing.
|
||||
*/
|
||||
} else {
|
||||
findnew_stack_state(state, offset, tsr->kind, &tsr->type,
|
||||
tsr->offset, tsr->imm_value);
|
||||
}
|
||||
|
||||
if (dreg == fbreg) {
|
||||
pr_debug_dtp("%s [%x] reg%d -> -%#x(stack)",
|
||||
dl->ins.name, insn_offset, sreg, -offset);
|
||||
} else {
|
||||
pr_debug_dtp("%s [%x] reg%d -> %#x(reg%d)",
|
||||
dl->ins.name, insn_offset, sreg, offset, dreg);
|
||||
}
|
||||
if (tsr->offset != 0)
|
||||
pr_debug_dtp(" reg%d offset %#x ->", sreg, tsr->offset);
|
||||
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/*
|
||||
* Ignore other transfers since it'd set a value in a struct
|
||||
* and won't change the type.
|
||||
*/
|
||||
}
|
||||
|
||||
static void update_store_insn_state(struct type_state *state,
|
||||
struct data_loc_info *dloc,
|
||||
struct disasm_line *dl,
|
||||
struct annotated_op_loc *src,
|
||||
struct annotated_op_loc *dst)
|
||||
{
|
||||
/*
|
||||
* Ignore transfers since it'd set a value in a struct
|
||||
* and won't change the type.
|
||||
*
|
||||
* Needs to update the pre-index and post-index addressing
|
||||
* modes for the destination register.
|
||||
*/
|
||||
int mem_spacing = 0;
|
||||
|
||||
if (!has_reg_type(state, dst->reg1))
|
||||
return;
|
||||
|
||||
/* Handle the first source register */
|
||||
propagate_store_reg_state(state, dloc, dl, src->reg1, dst, mem_spacing);
|
||||
|
||||
/* Handle the second source register (if any) */
|
||||
if (src->multi_regs) {
|
||||
mem_spacing = arm64__reg_size(dl->ops.source.raw);
|
||||
|
||||
if (mem_spacing >= 0)
|
||||
propagate_store_reg_state(state, dloc, dl, src->reg2,
|
||||
dst, mem_spacing);
|
||||
}
|
||||
|
||||
adjust_reg_index_state(state, dloc, dl, dst);
|
||||
}
|
||||
|
||||
|
|
@ -793,7 +930,7 @@ static void update_insn_state_arm64(struct type_state *state,
|
|||
update_load_insn_state(state, dloc, dl, src, dst);
|
||||
/* Register to memory transfers */
|
||||
else if (is_standard_store_insn(dl->ins.name))
|
||||
update_store_insn_state(state, dloc, dl, dst);
|
||||
update_store_insn_state(state, dloc, dl, src, dst);
|
||||
else
|
||||
invalidate_unhandled_insn_regs(state, dl, src, dst);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user