Skip to content

Commit 1f4cc4a

Browse files
Zecheng Linamhyung
authored andcommitted
perf annotate: Track arithmetic instructions on pointers
Track the arithmetic operations on registers with pointer types. We handle only add, sub and lea instructions. The original pointer information needs to be preserved for getting outermost struct types. For example, reg0 points to a struct cfs_rq, when we add 0x10 to reg0, it should preserve the information of struct cfs_rq + 0x10 in the register instead of a pointer type to the child field at 0x10. Details: 1. struct type_state_reg now includes an offset, indicating if the register points to the start or an internal part of its associated type. This offset is used in mem to reg and reg to stack mem transfers, and also applied to the final type offset. 2. lea offset(%sp/%fp), reg is now treated as taking the address of a stack variable. It worked fine in most cases, but an issue with this approach is the pointer type may not exist. 3. lea offset(%base), reg is handled by moving the type from %base and adding an offset, similar to an add operation followed by a mov reg to reg. 4. Non-stack variables from DWARF with non-zero offsets in their location expressions are now accepted with register offset tracking. Multi-register addressing modes in LEA are not supported. Signed-off-by: Zecheng Li <zecheng@google.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org>
1 parent 24a30ce commit 1f4cc4a

File tree

3 files changed

+152
-8
lines changed

3 files changed

+152
-8
lines changed

tools/perf/arch/x86/annotate/instructions.c

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ static void update_insn_state_x86(struct type_state *state,
248248
tsr = &state->regs[state->ret_reg];
249249
tsr->type = type_die;
250250
tsr->kind = TSR_KIND_TYPE;
251+
tsr->offset = 0;
251252
tsr->ok = true;
252253

253254
pr_debug_dtp("call [%x] return -> reg%d",
@@ -284,13 +285,27 @@ static void update_insn_state_x86(struct type_state *state,
284285
!strcmp(var_name, "this_cpu_off") &&
285286
tsr->kind == TSR_KIND_CONST) {
286287
tsr->kind = TSR_KIND_PERCPU_BASE;
288+
tsr->offset = 0;
287289
tsr->ok = true;
288290
imm_value = tsr->imm_value;
289291
}
290292
}
291293
else
292294
return;
293295

296+
/* Ignore add to non-pointer or non-const types */
297+
if (tsr->kind == TSR_KIND_POINTER ||
298+
(dwarf_tag(&tsr->type) == DW_TAG_pointer_type &&
299+
src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) {
300+
tsr->offset += imm_value;
301+
pr_debug_dtp("add [%x] offset %#"PRIx64" to reg%d",
302+
insn_offset, imm_value, dst->reg1);
303+
pr_debug_type_name(&tsr->type, tsr->kind);
304+
}
305+
306+
if (tsr->kind == TSR_KIND_CONST)
307+
tsr->imm_value += imm_value;
308+
294309
if (tsr->kind != TSR_KIND_PERCPU_BASE)
295310
return;
296311

@@ -302,6 +317,7 @@ static void update_insn_state_x86(struct type_state *state,
302317
*/
303318
tsr->type = type_die;
304319
tsr->kind = TSR_KIND_PERCPU_POINTER;
320+
tsr->offset = 0;
305321
tsr->ok = true;
306322

307323
pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d",
@@ -311,6 +327,106 @@ static void update_insn_state_x86(struct type_state *state,
311327
return;
312328
}
313329

330+
if (!strncmp(dl->ins.name, "sub", 3)) {
331+
u64 imm_value = -1ULL;
332+
333+
if (!has_reg_type(state, dst->reg1))
334+
return;
335+
336+
tsr = &state->regs[dst->reg1];
337+
tsr->copied_from = -1;
338+
339+
if (src->imm)
340+
imm_value = src->offset;
341+
else if (has_reg_type(state, src->reg1) &&
342+
state->regs[src->reg1].kind == TSR_KIND_CONST)
343+
imm_value = state->regs[src->reg1].imm_value;
344+
345+
if (tsr->kind == TSR_KIND_POINTER ||
346+
(dwarf_tag(&tsr->type) == DW_TAG_pointer_type &&
347+
src->reg1 != DWARF_REG_PC && tsr->kind == TSR_KIND_TYPE && !dst->mem_ref)) {
348+
tsr->offset -= imm_value;
349+
pr_debug_dtp("sub [%x] offset %#"PRIx64" to reg%d",
350+
insn_offset, imm_value, dst->reg1);
351+
pr_debug_type_name(&tsr->type, tsr->kind);
352+
}
353+
354+
if (tsr->kind == TSR_KIND_CONST)
355+
tsr->imm_value -= imm_value;
356+
357+
return;
358+
}
359+
360+
if (!strncmp(dl->ins.name, "lea", 3)) {
361+
int sreg = src->reg1;
362+
struct type_state_reg src_tsr;
363+
364+
if (!has_reg_type(state, sreg) ||
365+
!has_reg_type(state, dst->reg1) ||
366+
!src->mem_ref)
367+
return;
368+
369+
src_tsr = state->regs[sreg];
370+
tsr = &state->regs[dst->reg1];
371+
372+
tsr->copied_from = -1;
373+
tsr->ok = false;
374+
375+
/* Case 1: Based on stack pointer or frame pointer */
376+
if (sreg == fbreg || sreg == state->stack_reg) {
377+
struct type_state_stack *stack;
378+
int offset = src->offset - fboff;
379+
380+
stack = find_stack_state(state, offset);
381+
if (!stack)
382+
return;
383+
384+
tsr->type = stack->type;
385+
tsr->kind = TSR_KIND_POINTER;
386+
tsr->offset = offset - stack->offset;
387+
tsr->ok = true;
388+
389+
if (sreg == fbreg) {
390+
pr_debug_dtp("lea [%x] address of -%#x(stack) -> reg%d",
391+
insn_offset, -src->offset, dst->reg1);
392+
} else {
393+
pr_debug_dtp("lea [%x] address of %#x(reg%d) -> reg%d",
394+
insn_offset, src->offset, sreg, dst->reg1);
395+
}
396+
397+
pr_debug_type_name(&tsr->type, tsr->kind);
398+
}
399+
/* Case 2: Based on a register holding a typed pointer */
400+
else if (src_tsr.ok && (src_tsr.kind == TSR_KIND_POINTER ||
401+
(dwarf_tag(&src_tsr.type) == DW_TAG_pointer_type &&
402+
src_tsr.kind == TSR_KIND_TYPE))) {
403+
404+
if (src_tsr.kind == TSR_KIND_TYPE &&
405+
__die_get_real_type(&state->regs[sreg].type, &type_die) == NULL)
406+
return;
407+
408+
if (src_tsr.kind == TSR_KIND_POINTER)
409+
type_die = state->regs[sreg].type;
410+
411+
/* Check if the target type has a member at the new offset */
412+
if (die_get_member_type(&type_die,
413+
src->offset + src_tsr.offset, &type_die) == NULL)
414+
return;
415+
416+
tsr->type = src_tsr.type;
417+
tsr->kind = src_tsr.kind;
418+
tsr->offset = src->offset + src_tsr.offset;
419+
tsr->ok = true;
420+
421+
pr_debug_dtp("lea [%x] address of %s%#x(reg%d) -> reg%d",
422+
insn_offset, src->offset < 0 ? "-" : "",
423+
abs(src->offset), sreg, dst->reg1);
424+
425+
pr_debug_type_name(&tsr->type, tsr->kind);
426+
}
427+
return;
428+
}
429+
314430
if (strncmp(dl->ins.name, "mov", 3))
315431
return;
316432

@@ -345,6 +461,7 @@ static void update_insn_state_x86(struct type_state *state,
345461

346462
if (var_addr == 40) {
347463
tsr->kind = TSR_KIND_CANARY;
464+
tsr->offset = 0;
348465
tsr->ok = true;
349466

350467
pr_debug_dtp("mov [%x] stack canary -> reg%d\n",
@@ -361,6 +478,7 @@ static void update_insn_state_x86(struct type_state *state,
361478

362479
tsr->type = type_die;
363480
tsr->kind = TSR_KIND_TYPE;
481+
tsr->offset = 0;
364482
tsr->ok = true;
365483

366484
pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d",
@@ -372,6 +490,7 @@ static void update_insn_state_x86(struct type_state *state,
372490
if (src->imm) {
373491
tsr->kind = TSR_KIND_CONST;
374492
tsr->imm_value = src->offset;
493+
tsr->offset = 0;
375494
tsr->ok = true;
376495

377496
pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n",
@@ -388,6 +507,7 @@ static void update_insn_state_x86(struct type_state *state,
388507
tsr->type = state->regs[src->reg1].type;
389508
tsr->kind = state->regs[src->reg1].kind;
390509
tsr->imm_value = state->regs[src->reg1].imm_value;
510+
tsr->offset = state->regs[src->reg1].offset;
391511
tsr->ok = true;
392512

393513
/* To copy back the variable type later (hopefully) */
@@ -421,12 +541,14 @@ static void update_insn_state_x86(struct type_state *state,
421541
} else if (!stack->compound) {
422542
tsr->type = stack->type;
423543
tsr->kind = stack->kind;
544+
tsr->offset = 0;
424545
tsr->ok = true;
425546
} else if (die_get_member_type(&stack->type,
426547
offset - stack->offset,
427548
&type_die)) {
428549
tsr->type = type_die;
429550
tsr->kind = TSR_KIND_TYPE;
551+
tsr->offset = 0;
430552
tsr->ok = true;
431553
} else {
432554
tsr->ok = false;
@@ -446,9 +568,10 @@ static void update_insn_state_x86(struct type_state *state,
446568
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
447569
state->regs[sreg].kind == TSR_KIND_TYPE &&
448570
die_deref_ptr_type(&state->regs[sreg].type,
449-
src->offset, &type_die)) {
571+
src->offset + state->regs[sreg].offset, &type_die)) {
450572
tsr->type = type_die;
451573
tsr->kind = TSR_KIND_TYPE;
574+
tsr->offset = 0;
452575
tsr->ok = true;
453576

454577
pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",
@@ -459,9 +582,10 @@ static void update_insn_state_x86(struct type_state *state,
459582
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
460583
state->regs[sreg].kind == TSR_KIND_POINTER &&
461584
die_get_member_type(&state->regs[sreg].type,
462-
src->offset, &type_die)) {
585+
src->offset + state->regs[sreg].offset, &type_die)) {
463586
tsr->type = state->regs[sreg].type;
464587
tsr->kind = TSR_KIND_TYPE;
588+
tsr->offset = src->offset + state->regs[sreg].offset;
465589
tsr->ok = true;
466590

467591
pr_debug_dtp("mov [%x] addr %#x(reg%d) -> reg%d",
@@ -486,6 +610,7 @@ static void update_insn_state_x86(struct type_state *state,
486610

487611
tsr->type = type_die;
488612
tsr->kind = TSR_KIND_TYPE;
613+
tsr->offset = 0;
489614
tsr->ok = true;
490615

491616
pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d",
@@ -517,6 +642,7 @@ static void update_insn_state_x86(struct type_state *state,
517642
die_get_member_type(&type_die, offset, &type_die)) {
518643
tsr->type = type_die;
519644
tsr->kind = TSR_KIND_TYPE;
645+
tsr->offset = 0;
520646
tsr->ok = true;
521647

522648
if (src->multi_regs) {
@@ -539,6 +665,7 @@ static void update_insn_state_x86(struct type_state *state,
539665
src->offset, &type_die)) {
540666
tsr->type = type_die;
541667
tsr->kind = TSR_KIND_TYPE;
668+
tsr->offset = 0;
542669
tsr->ok = true;
543670

544671
pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d",
@@ -561,6 +688,7 @@ static void update_insn_state_x86(struct type_state *state,
561688
&var_name, &offset) &&
562689
!strcmp(var_name, "__per_cpu_offset")) {
563690
tsr->kind = TSR_KIND_PERCPU_BASE;
691+
tsr->offset = 0;
564692
tsr->ok = true;
565693

566694
pr_debug_dtp("mov [%x] percpu base reg%d\n",
@@ -609,6 +737,11 @@ static void update_insn_state_x86(struct type_state *state,
609737
pr_debug_dtp("mov [%x] reg%d -> %#x(reg%d)",
610738
insn_offset, src->reg1, offset, dst->reg1);
611739
}
740+
if (tsr->offset != 0) {
741+
pr_debug_dtp(" reg%d offset %#x ->",
742+
src->reg1, tsr->offset);
743+
}
744+
612745
pr_debug_type_name(&tsr->type, tsr->kind);
613746
}
614747
/*

tools/perf/util/annotate-data.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
905905
insn_offset, -offset);
906906
}
907907
pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
908-
} else if (has_reg_type(state, var->reg) && var->offset == 0) {
908+
} else if (has_reg_type(state, var->reg)) {
909909
struct type_state_reg *reg;
910910
Dwarf_Die orig_type;
911911

@@ -921,6 +921,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
921921
!is_better_type(&reg->type, &mem_die))
922922
continue;
923923

924+
reg->offset = -var->offset;
924925
reg->type = mem_die;
925926
reg->kind = TSR_KIND_POINTER;
926927
reg->ok = true;
@@ -932,13 +933,17 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
932933
}
933934

934935
orig_type = reg->type;
935-
936+
/*
937+
* var->offset + reg value is the beginning of the struct
938+
* reg->offset is the offset the reg points
939+
*/
940+
reg->offset = -var->offset;
936941
reg->type = mem_die;
937942
reg->kind = TSR_KIND_TYPE;
938943
reg->ok = true;
939944

940-
pr_debug_dtp("var [%"PRIx64"] reg%d",
941-
insn_offset, var->reg);
945+
pr_debug_dtp("var [%"PRIx64"] reg%d offset %x",
946+
insn_offset, var->reg, var->offset);
942947
pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
943948

944949
/*
@@ -1126,7 +1131,7 @@ static enum type_match_result check_matching_type(struct type_state *state,
11261131
if (__die_get_real_type(&state->regs[reg].type, type_die) == NULL)
11271132
return PERF_TMR_NO_POINTER;
11281133

1129-
dloc->type_offset = dloc->op->offset;
1134+
dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
11301135

11311136
if (dwarf_tag(type_die) == DW_TAG_typedef)
11321137
die_get_real_type(type_die, &sized_type);
@@ -1155,7 +1160,7 @@ static enum type_match_result check_matching_type(struct type_state *state,
11551160
*/
11561161
*type_die = state->regs[reg].type;
11571162

1158-
dloc->type_offset = dloc->op->offset;
1163+
dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
11591164

11601165
/* Get the size of the actual type */
11611166
if (dwarf_aggregate_size(type_die, &size) < 0 ||

tools/perf/util/annotate-data.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ extern struct annotated_data_stat ann_data_stat;
174174
struct type_state_reg {
175175
Dwarf_Die type;
176176
u32 imm_value;
177+
/*
178+
* The offset within the struct that the register points to.
179+
* A value of 0 means the register points to the beginning.
180+
* type_offset = op->offset + reg->offset
181+
*/
182+
s32 offset;
177183
bool ok;
178184
bool caller_saved;
179185
u8 kind;

0 commit comments

Comments
 (0)