Commit 06b2ce75 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo

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: default avatarNamhyung 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.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 90429524
...@@ -46,6 +46,62 @@ static void pr_debug_type_name(Dwarf_Die *die) ...@@ -46,6 +46,62 @@ static void pr_debug_type_name(Dwarf_Die *die)
free(str); 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. * 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 * 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, ...@@ -262,6 +318,123 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset,
return 0; 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 */ /* The result will be saved in @type_die */
static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
{ {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "debug.h" #include "debug.h"
#include "dwarf-aux.h" #include "dwarf-aux.h"
#include "dwarf-regs.h"
#include "strbuf.h" #include "strbuf.h"
#include "string2.h" #include "string2.h"
...@@ -1190,6 +1191,8 @@ static int reg_from_dwarf_op(Dwarf_Op *op) ...@@ -1190,6 +1191,8 @@ static int reg_from_dwarf_op(Dwarf_Op *op)
case DW_OP_regx: case DW_OP_regx:
case DW_OP_bregx: case DW_OP_bregx:
return op->number; return op->number;
case DW_OP_fbreg:
return DWARF_REG_FB;
default: default:
break; break;
} }
...@@ -1203,6 +1206,7 @@ static int offset_from_dwarf_op(Dwarf_Op *op) ...@@ -1203,6 +1206,7 @@ static int offset_from_dwarf_op(Dwarf_Op *op)
case DW_OP_regx: case DW_OP_regx:
return 0; return 0;
case DW_OP_breg0 ... DW_OP_breg31: case DW_OP_breg0 ... DW_OP_breg31:
case DW_OP_fbreg:
return op->number; return op->number;
case DW_OP_bregx: case DW_OP_bregx:
return op->number2; return op->number2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment