perf annotate-data: Maintain variable type info
As it collected basic block and variable information in each scope, it now can build a state table to find matching variable at the location. The struct type_state is to keep the type info saved in each register and stack slot. The update_var_state() updates the table when it finds variables in the current address. It expects die_collect_vars() filled a list of variables with type info and starting address. Signed-off-by: Namhyung Kim <namhyung@kernel.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Link: https://lore.kernel.org/r/20240319055115.4063940-10-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
90429524f3
commit
06b2ce7538
@ -46,6 +46,62 @@ static void pr_debug_type_name(Dwarf_Die *die)
|
||||
free(str);
|
||||
}
|
||||
|
||||
/* Type information in a register, valid when ok is true */
|
||||
struct type_state_reg {
|
||||
Dwarf_Die type;
|
||||
bool ok;
|
||||
};
|
||||
|
||||
/* Type information in a stack location, dynamically allocated */
|
||||
struct type_state_stack {
|
||||
struct list_head list;
|
||||
Dwarf_Die type;
|
||||
int offset;
|
||||
int size;
|
||||
bool compound;
|
||||
};
|
||||
|
||||
/* FIXME: This should be arch-dependent */
|
||||
#define TYPE_STATE_MAX_REGS 16
|
||||
|
||||
/*
|
||||
* State table to maintain type info in each register and stack location.
|
||||
* It'll be updated when new variable is allocated or type info is moved
|
||||
* to a new location (register or stack). As it'd be used with the
|
||||
* shortest path of basic blocks, it only maintains a single table.
|
||||
*/
|
||||
struct type_state {
|
||||
struct type_state_reg regs[TYPE_STATE_MAX_REGS];
|
||||
struct list_head stack_vars;
|
||||
};
|
||||
|
||||
static bool has_reg_type(struct type_state *state, int reg)
|
||||
{
|
||||
return (unsigned)reg < ARRAY_SIZE(state->regs);
|
||||
}
|
||||
|
||||
/* These declarations will be remove once they are changed to static */
|
||||
void init_type_state(struct type_state *state, struct arch *arch __maybe_unused);
|
||||
void exit_type_state(struct type_state *state);
|
||||
void update_var_state(struct type_state *state, struct data_loc_info *dloc,
|
||||
u64 addr, u64 insn_offset, struct die_var_type *var_types);
|
||||
|
||||
void init_type_state(struct type_state *state, struct arch *arch __maybe_unused)
|
||||
{
|
||||
memset(state, 0, sizeof(*state));
|
||||
INIT_LIST_HEAD(&state->stack_vars);
|
||||
}
|
||||
|
||||
void exit_type_state(struct type_state *state)
|
||||
{
|
||||
struct type_state_stack *stack, *tmp;
|
||||
|
||||
list_for_each_entry_safe(stack, tmp, &state->stack_vars, list) {
|
||||
list_del(&stack->list);
|
||||
free(stack);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare type name and size to maintain them in a tree.
|
||||
* I'm not sure if DWARF would have information of a single type in many
|
||||
@ -262,6 +318,123 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct type_state_stack *find_stack_state(struct type_state *state,
|
||||
int offset)
|
||||
{
|
||||
struct type_state_stack *stack;
|
||||
|
||||
list_for_each_entry(stack, &state->stack_vars, list) {
|
||||
if (offset == stack->offset)
|
||||
return stack;
|
||||
|
||||
if (stack->compound && stack->offset < offset &&
|
||||
offset < stack->offset + stack->size)
|
||||
return stack;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_stack_state(struct type_state_stack *stack, int offset,
|
||||
Dwarf_Die *type_die)
|
||||
{
|
||||
int tag;
|
||||
Dwarf_Word size;
|
||||
|
||||
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;
|
||||
|
||||
switch (tag) {
|
||||
case DW_TAG_structure_type:
|
||||
case DW_TAG_union_type:
|
||||
stack->compound = true;
|
||||
break;
|
||||
default:
|
||||
stack->compound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct type_state_stack *findnew_stack_state(struct type_state *state,
|
||||
int offset, Dwarf_Die *type_die)
|
||||
{
|
||||
struct type_state_stack *stack = find_stack_state(state, offset);
|
||||
|
||||
if (stack) {
|
||||
set_stack_state(stack, offset, type_die);
|
||||
return stack;
|
||||
}
|
||||
|
||||
stack = malloc(sizeof(*stack));
|
||||
if (stack) {
|
||||
set_stack_state(stack, offset, type_die);
|
||||
list_add(&stack->list, &state->stack_vars);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* update_var_state - Update type state using given variables
|
||||
* @state: type state table
|
||||
* @dloc: data location info
|
||||
* @addr: instruction address to match with variable
|
||||
* @insn_offset: instruction offset (for debug)
|
||||
* @var_types: list of variables with type info
|
||||
*
|
||||
* This function fills the @state table using @var_types info. Each variable
|
||||
* is used only at the given location and updates an entry in the table.
|
||||
*/
|
||||
void update_var_state(struct type_state *state, struct data_loc_info *dloc,
|
||||
u64 addr, u64 insn_offset, struct die_var_type *var_types)
|
||||
{
|
||||
Dwarf_Die mem_die;
|
||||
struct die_var_type *var;
|
||||
int fbreg = dloc->fbreg;
|
||||
int fb_offset = 0;
|
||||
|
||||
if (dloc->fb_cfa) {
|
||||
if (die_get_cfa(dloc->di->dbg, addr, &fbreg, &fb_offset) < 0)
|
||||
fbreg = -1;
|
||||
}
|
||||
|
||||
for (var = var_types; var != NULL; var = var->next) {
|
||||
if (var->addr != 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) {
|
||||
findnew_stack_state(state, var->offset, &mem_die);
|
||||
|
||||
pr_debug_dtp("var [%"PRIx64"] -%#x(stack) type=",
|
||||
insn_offset, -var->offset);
|
||||
pr_debug_type_name(&mem_die);
|
||||
} else if (var->reg == fbreg) {
|
||||
findnew_stack_state(state, var->offset - fb_offset, &mem_die);
|
||||
|
||||
pr_debug_dtp("var [%"PRIx64"] -%#x(stack) type=",
|
||||
insn_offset, -var->offset + fb_offset);
|
||||
pr_debug_type_name(&mem_die);
|
||||
} else if (has_reg_type(state, var->reg) && var->offset == 0) {
|
||||
struct type_state_reg *reg;
|
||||
|
||||
reg = &state->regs[var->reg];
|
||||
reg->type = mem_die;
|
||||
reg->ok = true;
|
||||
|
||||
pr_debug_dtp("var [%"PRIx64"] reg%d type=",
|
||||
insn_offset, var->reg);
|
||||
pr_debug_type_name(&mem_die);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The result will be saved in @type_die */
|
||||
static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
|
||||
{
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <stdlib.h>
|
||||
#include "debug.h"
|
||||
#include "dwarf-aux.h"
|
||||
#include "dwarf-regs.h"
|
||||
#include "strbuf.h"
|
||||
#include "string2.h"
|
||||
|
||||
@ -1190,6 +1191,8 @@ static int reg_from_dwarf_op(Dwarf_Op *op)
|
||||
case DW_OP_regx:
|
||||
case DW_OP_bregx:
|
||||
return op->number;
|
||||
case DW_OP_fbreg:
|
||||
return DWARF_REG_FB;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1203,6 +1206,7 @@ static int offset_from_dwarf_op(Dwarf_Op *op)
|
||||
case DW_OP_regx:
|
||||
return 0;
|
||||
case DW_OP_breg0 ... DW_OP_breg31:
|
||||
case DW_OP_fbreg:
|
||||
return op->number;
|
||||
case DW_OP_bregx:
|
||||
return op->number2;
|
||||
|
Loading…
x
Reference in New Issue
Block a user