summaryrefslogtreecommitdiff
path: root/tools/perf/util/annotate-data.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/annotate-data.c')
-rw-r--r--tools/perf/util/annotate-data.c273
1 files changed, 199 insertions, 74 deletions
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 976abedca09e..1eff0a27237d 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -4,7 +4,7 @@
*
* Written by Namhyung Kim <namhyung@kernel.org>
*/
-
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
@@ -58,6 +58,10 @@ void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind)
case TSR_KIND_CONST:
pr_info(" constant\n");
return;
+ case TSR_KIND_PERCPU_POINTER:
+ pr_info(" percpu pointer");
+ /* it also prints the type info */
+ break;
case TSR_KIND_POINTER:
pr_info(" pointer");
/* it also prints the type info */
@@ -156,12 +160,12 @@ bool has_reg_type(struct type_state *state, int reg)
return (unsigned)reg < ARRAY_SIZE(state->regs);
}
-static void init_type_state(struct type_state *state, struct arch *arch)
+static void init_type_state(struct type_state *state, const struct arch *arch)
{
memset(state, 0, sizeof(*state));
INIT_LIST_HEAD(&state->stack_vars);
- if (arch__is(arch, "x86")) {
+ if (arch__is_x86(arch)) {
state->regs[0].caller_saved = true;
state->regs[1].caller_saved = true;
state->regs[2].caller_saved = true;
@@ -314,6 +318,40 @@ static void delete_members(struct annotated_member *member)
}
}
+static int fill_member_name(char *buf, size_t sz, struct annotated_member *m,
+ int offset, bool first)
+{
+ struct annotated_member *child;
+
+ if (list_empty(&m->children))
+ return 0;
+
+ list_for_each_entry(child, &m->children, node) {
+ int len;
+
+ if (offset < child->offset || offset >= child->offset + child->size)
+ continue;
+
+ /* It can have anonymous struct/union members */
+ if (child->var_name) {
+ len = scnprintf(buf, sz, "%s%s",
+ first ? "" : ".", child->var_name);
+ first = false;
+ } else {
+ len = 0;
+ }
+
+ return fill_member_name(buf + len, sz - len, child, offset, first) + len;
+ }
+ return 0;
+}
+
+int annotated_data_type__get_member_name(struct annotated_data_type *adt,
+ char *buf, size_t sz, int member_offset)
+{
+ return fill_member_name(buf, sz, &adt->self, member_offset, /*first=*/true);
+}
+
static struct annotated_data_type *dso__findnew_data_type(struct dso *dso,
Dwarf_Die *type_die)
{
@@ -417,13 +455,6 @@ static const char *match_result_str(enum type_match_result tmr)
}
}
-static bool is_pointer_type(Dwarf_Die *type_die)
-{
- int tag = dwarf_tag(type_die);
-
- return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type;
-}
-
static bool is_compound_type(Dwarf_Die *type_die)
{
int tag = dwarf_tag(type_die);
@@ -436,19 +467,24 @@ static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b)
{
Dwarf_Word size_a, size_b;
Dwarf_Die die_a, die_b;
+ Dwarf_Die ptr_a, ptr_b;
+ Dwarf_Die *ptr_type_a, *ptr_type_b;
+
+ ptr_type_a = die_get_pointer_type(type_a, &ptr_a);
+ ptr_type_b = die_get_pointer_type(type_b, &ptr_b);
/* pointer type is preferred */
- if (is_pointer_type(type_a) != is_pointer_type(type_b))
- return is_pointer_type(type_b);
+ if ((ptr_type_a != NULL) != (ptr_type_b != NULL))
+ return ptr_type_b != NULL;
- if (is_pointer_type(type_b)) {
+ if (ptr_type_b) {
/*
* We want to compare the target type, but 'void *' can fail to
* get the target type.
*/
- if (die_get_real_type(type_a, &die_a) == NULL)
+ if (die_get_real_type(ptr_type_a, &die_a) == NULL)
return true;
- if (die_get_real_type(type_b, &die_b) == NULL)
+ if (die_get_real_type(ptr_type_b, &die_b) == NULL)
return false;
type_a = &die_a;
@@ -488,7 +524,7 @@ static enum type_match_result check_variable(struct data_loc_info *dloc,
needs_pointer = false;
else if (reg == dloc->fbreg || is_fbreg)
needs_pointer = false;
- else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP)
+ else if (arch__is_x86(dloc->arch) && reg == X86_REG_SP)
needs_pointer = false;
/* Get the type of the variable */
@@ -501,7 +537,7 @@ static enum type_match_result check_variable(struct data_loc_info *dloc,
* and local variables are accessed directly without a pointer.
*/
if (needs_pointer) {
- if (!is_pointer_type(type_die) ||
+ if (die_get_pointer_type(type_die, type_die) == NULL ||
__die_get_real_type(type_die, type_die) == NULL)
return PERF_TMR_NO_POINTER;
}
@@ -539,25 +575,35 @@ struct type_state_stack *find_stack_state(struct type_state *state,
}
void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
- Dwarf_Die *type_die)
+ Dwarf_Die *type_die, int ptr_offset)
{
int tag;
Dwarf_Word size;
- if (dwarf_aggregate_size(type_die, &size) < 0)
+ if (kind == TSR_KIND_POINTER) {
+ /* TODO: arch-dependent pointer size */
+ size = sizeof(void *);
+ }
+ else if (dwarf_aggregate_size(type_die, &size) < 0)
size = 0;
- tag = dwarf_tag(type_die);
-
stack->type = *type_die;
stack->size = size;
stack->offset = offset;
+ stack->ptr_offset = ptr_offset;
stack->kind = kind;
+ if (kind == TSR_KIND_POINTER) {
+ stack->compound = false;
+ return;
+ }
+
+ tag = dwarf_tag(type_die);
+
switch (tag) {
case DW_TAG_structure_type:
case DW_TAG_union_type:
- stack->compound = (kind != TSR_KIND_POINTER);
+ stack->compound = (kind != TSR_KIND_PERCPU_POINTER);
break;
default:
stack->compound = false;
@@ -567,18 +613,19 @@ void set_stack_state(struct type_state_stack *stack, int offset, u8 kind,
struct type_state_stack *findnew_stack_state(struct type_state *state,
int offset, u8 kind,
- Dwarf_Die *type_die)
+ Dwarf_Die *type_die,
+ int ptr_offset)
{
struct type_state_stack *stack = find_stack_state(state, offset);
if (stack) {
- set_stack_state(stack, offset, kind, type_die);
+ set_stack_state(stack, offset, kind, type_die, ptr_offset);
return stack;
}
stack = malloc(sizeof(*stack));
if (stack) {
- set_stack_state(stack, offset, kind, type_die);
+ set_stack_state(stack, offset, kind, type_die, ptr_offset);
list_add(&stack->list, &state->stack_vars);
}
return stack;
@@ -727,12 +774,7 @@ static void global_var__collect(struct data_loc_info *dloc)
if (!dwarf_offdie(dwarf, pos->die_off, &type_die))
continue;
- if (!get_global_var_info(dloc, pos->addr, &var_name,
- &var_offset))
- continue;
-
- if (var_offset != 0)
- continue;
+ get_global_var_info(dloc, pos->addr, &var_name, &var_offset);
global_var__add(dloc, pos->addr, var_name, &type_die);
}
@@ -767,9 +809,8 @@ bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc,
}
/* Try to get the variable by address first */
- if (die_find_variable_by_addr(cu_die, var_addr, &var_die, &offset) &&
- check_variable(dloc, &var_die, type_die, DWARF_REG_PC, offset,
- /*is_fbreg=*/false) == PERF_TMR_OK) {
+ if (die_find_variable_by_addr(cu_die, var_addr, &var_die, type_die,
+ &offset)) {
var_name = dwarf_diename(&var_die);
*var_offset = offset;
goto ok;
@@ -799,6 +840,18 @@ static bool die_is_same(Dwarf_Die *die_a, Dwarf_Die *die_b)
return (die_a->cu == die_b->cu) && (die_a->addr == die_b->addr);
}
+static void tsr_set_lifetime(struct type_state_reg *tsr,
+ const struct die_var_type *var)
+{
+ if (var && var->has_range && var->end > var->addr) {
+ tsr->lifetime_active = true;
+ tsr->lifetime_end = var->end;
+ } else {
+ tsr->lifetime_active = false;
+ tsr->lifetime_end = 0;
+ }
+}
+
/**
* update_var_state - Update type state using given variables
* @state: type state table
@@ -824,16 +877,31 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
}
for (var = var_types; var != NULL; var = var->next) {
- if (var->addr != addr)
- continue;
+ /* Check if addr falls within the variable's valid range */
+ if (var->has_range) {
+ if (addr < var->addr || (var->end && addr >= var->end))
+ continue;
+ } else {
+ if (addr != var->addr)
+ continue;
+ }
/* Get the type DIE using the offset */
if (!dwarf_offdie(dloc->di->dbg, var->die_off, &mem_die))
continue;
- if (var->reg == DWARF_REG_FB || var->reg == fbreg) {
+ if (var->reg == DWARF_REG_FB || var->reg == fbreg || var->reg == state->stack_reg) {
+ Dwarf_Die ptr_die;
+ Dwarf_Die *ptr_type;
int offset = var->offset;
struct type_state_stack *stack;
+ ptr_type = die_get_pointer_type(&mem_die, &ptr_die);
+
+ /* If the reg location holds the pointer value, dereference the type */
+ if (!var->is_reg_var_addr && ptr_type &&
+ __die_get_real_type(ptr_type, &mem_die) == NULL)
+ continue;
+
if (var->reg != DWARF_REG_FB)
offset -= fb_offset;
@@ -843,29 +911,57 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo
continue;
findnew_stack_state(state, offset, TSR_KIND_TYPE,
- &mem_die);
+ &mem_die, /*ptr_offset=*/0);
- pr_debug_dtp("var [%"PRIx64"] -%#x(stack)",
- insn_offset, -offset);
+ if (var->reg == state->stack_reg) {
+ pr_debug_dtp("var [%"PRIx64"] %#x(reg%d)",
+ insn_offset, offset, state->stack_reg);
+ } else {
+ pr_debug_dtp("var [%"PRIx64"] -%#x(stack)",
+ insn_offset, -offset);
+ }
pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
- } else if (has_reg_type(state, var->reg) && var->offset == 0) {
+ } else if (has_reg_type(state, var->reg)) {
struct type_state_reg *reg;
Dwarf_Die orig_type;
reg = &state->regs[var->reg];
if (reg->ok && reg->kind == TSR_KIND_TYPE &&
- !is_better_type(&reg->type, &mem_die))
+ (!is_better_type(&reg->type, &mem_die) || var->is_reg_var_addr))
continue;
- orig_type = reg->type;
+ /* Handle address registers with TSR_KIND_POINTER */
+ if (var->is_reg_var_addr) {
+ if (reg->ok && reg->kind == TSR_KIND_POINTER &&
+ !is_better_type(&reg->type, &mem_die))
+ continue;
+
+ reg->offset = -var->offset;
+ reg->type = mem_die;
+ reg->kind = TSR_KIND_POINTER;
+ reg->ok = true;
+ tsr_set_lifetime(reg, var);
+
+ pr_debug_dtp("var [%"PRIx64"] reg%d addr offset %x",
+ insn_offset, var->reg, var->offset);
+ pr_debug_type_name(&mem_die, TSR_KIND_POINTER);
+ continue;
+ }
+ orig_type = reg->type;
+ /*
+ * var->offset + reg value is the beginning of the struct
+ * reg->offset is the offset the reg points
+ */
+ reg->offset = -var->offset;
reg->type = mem_die;
reg->kind = TSR_KIND_TYPE;
reg->ok = true;
+ tsr_set_lifetime(reg, var);
- pr_debug_dtp("var [%"PRIx64"] reg%d",
- insn_offset, var->reg);
+ pr_debug_dtp("var [%"PRIx64"] reg%d offset %x",
+ insn_offset, var->reg, var->offset);
pr_debug_type_name(&mem_die, TSR_KIND_TYPE);
/*
@@ -991,7 +1087,7 @@ static void delete_var_types(struct die_var_type *var_types)
/* should match to is_stack_canary() in util/annotate.c */
static void setup_stack_canary(struct data_loc_info *dloc)
{
- if (arch__is(dloc->arch, "x86")) {
+ if (arch__is_x86(dloc->arch)) {
dloc->op->segment = INSN_SEG_X86_GS;
dloc->op->imm = true;
dloc->op->offset = 40;
@@ -1030,7 +1126,9 @@ again:
goto check_non_register;
if (state->regs[reg].kind == TSR_KIND_TYPE) {
+ Dwarf_Die ptr_die;
Dwarf_Die sized_type;
+ Dwarf_Die *ptr_type;
struct strbuf sb;
strbuf_init(&sb, 32);
@@ -1042,7 +1140,8 @@ again:
* Normal registers should hold a pointer (or array) to
* dereference a memory location.
*/
- if (!is_pointer_type(&state->regs[reg].type)) {
+ ptr_type = die_get_pointer_type(&state->regs[reg].type, &ptr_die);
+ if (!ptr_type) {
if (dloc->op->offset < 0 && reg != state->stack_reg)
goto check_kernel;
@@ -1050,10 +1149,10 @@ again:
}
/* Remove the pointer and get the target type */
- if (__die_get_real_type(&state->regs[reg].type, type_die) == NULL)
+ if (__die_get_real_type(ptr_type, type_die) == NULL)
return PERF_TMR_NO_POINTER;
- dloc->type_offset = dloc->op->offset;
+ dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
if (dwarf_tag(type_die) == DW_TAG_typedef)
die_get_real_type(type_die, &sized_type);
@@ -1069,6 +1168,30 @@ again:
}
if (state->regs[reg].kind == TSR_KIND_POINTER) {
+ struct strbuf sb;
+
+ strbuf_init(&sb, 32);
+ die_get_typename_from_type(&state->regs[reg].type, &sb);
+ pr_debug_dtp("(ptr->%s)", sb.buf);
+ strbuf_release(&sb);
+
+ /*
+ * Register holds a pointer (address) to the target variable.
+ * The type is the type of the variable it points to.
+ */
+ *type_die = state->regs[reg].type;
+
+ dloc->type_offset = dloc->op->offset + state->regs[reg].offset;
+
+ /* Get the size of the actual type */
+ if (dwarf_aggregate_size(type_die, &size) < 0 ||
+ (unsigned)dloc->type_offset >= size)
+ return PERF_TMR_BAD_OFFSET;
+
+ return PERF_TMR_OK;
+ }
+
+ if (state->regs[reg].kind == TSR_KIND_PERCPU_POINTER) {
pr_debug_dtp("percpu ptr");
/*
@@ -1126,11 +1249,16 @@ again:
return PERF_TMR_BAIL_OUT;
}
+ 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)
+ goto check_kernel;
+ }
check_non_register:
- if (reg == dloc->fbreg) {
+ if (reg == dloc->fbreg || reg == state->stack_reg) {
struct type_state_stack *stack;
- pr_debug_dtp("fbreg");
+ pr_debug_dtp("%s", reg == dloc->fbreg ? "fbreg" : "stack");
stack = find_stack_state(state, dloc->type_offset);
if (stack == NULL) {
@@ -1207,7 +1335,7 @@ check_kernel:
/* Direct this-cpu access like "%gs:0x34740" */
if (dloc->op->segment == INSN_SEG_X86_GS && dloc->op->imm &&
- arch__is(dloc->arch, "x86")) {
+ arch__is_x86(dloc->arch)) {
pr_debug_dtp("this-cpu var");
addr = dloc->op->offset;
@@ -1293,7 +1421,7 @@ out:
static int arch_supports_insn_tracking(struct data_loc_info *dloc)
{
- if ((arch__is(dloc->arch, "x86")) || (arch__is(dloc->arch, "powerpc")))
+ if ((arch__is_x86(dloc->arch)) || (arch__is_powerpc(dloc->arch)))
return 1;
return 0;
}
@@ -1497,12 +1625,13 @@ retry:
if (reg == DWARF_REG_PC) {
if (!die_find_variable_by_addr(&scopes[i], dloc->var_addr,
- &var_die, &type_offset))
+ &var_die, &mem_die,
+ &type_offset))
continue;
} else {
/* Look up variables/parameters in this scope */
if (!die_find_variable_by_reg(&scopes[i], pc, reg,
- &type_offset, is_fbreg, &var_die))
+ &mem_die, &type_offset, is_fbreg, &var_die))
continue;
}
@@ -1510,26 +1639,22 @@ retry:
dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die),
i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i]));
- /* Found a variable, see if it's correct */
- result = check_variable(dloc, &var_die, &mem_die, reg, type_offset, is_fbreg);
- if (result == PERF_TMR_OK) {
- if (reg == DWARF_REG_PC) {
- pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n",
- dloc->var_addr, type_offset);
- } else if (reg == DWARF_REG_FB || is_fbreg) {
- pr_debug_dtp("stack_offset=%#x type_offset=%#x\n",
- fb_offset, type_offset);
- } else {
- pr_debug_dtp("type_offset=%#x\n", type_offset);
- }
-
- if (!found || is_better_type(type_die, &mem_die)) {
- *type_die = mem_die;
- dloc->type_offset = type_offset;
- found = true;
- }
+ if (reg == DWARF_REG_PC) {
+ pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n",
+ dloc->var_addr, type_offset);
+ } else if (reg == DWARF_REG_FB || is_fbreg) {
+ pr_debug_dtp("stack_offset=%#x type_offset=%#x\n",
+ fb_offset, type_offset);
} else {
- pr_debug_dtp("failed: %s\n", match_result_str(result));
+ pr_debug_dtp("type_offset=%#x\n", type_offset);
+ }
+
+ if (!found || dloc->type_offset < type_offset ||
+ (dloc->type_offset == type_offset &&
+ !is_better_type(&mem_die, type_die))) {
+ *type_die = mem_die;
+ dloc->type_offset = type_offset;
+ found = true;
}
pr_debug_location(&var_die, pc, reg);