lkml.org 
[lkml]   [2023]   [Oct]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 39/48] perf annotate-data: Maintain variable type info
    Date
    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>
    ---
    tools/perf/util/annotate-data.c | 155 ++++++++++++++++++++++++++++++++
    tools/perf/util/annotate-data.h | 29 ++++++
    tools/perf/util/dwarf-aux.c | 4 +
    3 files changed, 188 insertions(+)

    diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
    index 90793cbb6aa0..a88d2cdafa08 100644
    --- a/tools/perf/util/annotate-data.c
    +++ b/tools/perf/util/annotate-data.c
    @@ -40,6 +40,57 @@ struct annotated_data_type stackop_type = {
    /* Data type collection debug statistics */
    struct annotated_data_stat ann_data_stat;

    +/* Type information in a register, valid when ok is true */
    +struct type_state_reg {
    + Dwarf_Die type;
    + bool ok;
    + bool scratch;
    +};
    +
    +/* 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);
    +}
    +
    +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
    @@ -255,6 +306,110 @@ 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 update
    + * @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, 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);
    + } else if (var->reg == fbreg) {
    + findnew_stack_state(state, var->offset - fb_offset, &mem_die);
    + } else if (has_reg_type(state, var->reg)) {
    + struct type_state_reg *reg;
    +
    + reg = &state->regs[var->reg];
    + reg->type = mem_die;
    + reg->ok = true;
    + }
    + }
    +}
    +
    /* The result will be saved in @type_die */
    static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die)
    {
    diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h
    index ad6493ea2c8e..7fbb9eb2e96f 100644
    --- a/tools/perf/util/annotate-data.h
    +++ b/tools/perf/util/annotate-data.h
    @@ -8,9 +8,12 @@
    #include <linux/types.h>

    struct annotated_op_loc;
    +struct arch;
    struct debuginfo;
    +struct die_var_type;
    struct evsel;
    struct map_symbol;
    +struct type_state;

    /**
    * struct annotated_member - Type of member field
    @@ -146,6 +149,16 @@ int annotated_data_type__update_samples(struct annotated_data_type *adt,
    /* Release all data type information in the tree */
    void annotated_data_type__tree_delete(struct rb_root *root);

    +/* Initialize type state table */
    +void init_type_state(struct type_state *state, struct arch *arch);
    +
    +/* Destroy type state table */
    +void exit_type_state(struct type_state *state);
    +
    +/* Update type state table using variables */
    +void update_var_state(struct type_state *state, struct data_loc_info *dloc,
    + u64 addr, struct die_var_type *var_types);
    +
    #else /* HAVE_DWARF_SUPPORT */

    static inline struct annotated_data_type *
    @@ -168,6 +181,22 @@ static inline void annotated_data_type__tree_delete(struct rb_root *root __maybe
    {
    }

    +static inline void init_type_state(struct type_state *state __maybe_unused,
    + struct arch *arch __maybe_unused)
    +{
    +}
    +
    +static inline void exit_type_state(struct type_state *state __maybe_unused)
    +{
    +}
    +
    +static inline void update_var_state(struct type_state *state __maybe_unused,
    + struct data_loc_info *dloc __maybe_unused,
    + u64 addr __maybe_unused,
    + struct die_var_type *var_types __maybe_unused)
    +{
    +}
    +
    #endif /* HAVE_DWARF_SUPPORT */

    #endif /* _PERF_ANNOTATE_DATA_H */
    diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
    index 5ec895e0a069..923e974ad18e 100644
    --- a/tools/perf/util/dwarf-aux.c
    +++ b/tools/perf/util/dwarf-aux.c
    @@ -9,6 +9,7 @@
    #include <stdlib.h>
    #include "debug.h"
    #include "dwarf-aux.h"
    +#include "dwarf-regs.h"
    #include "strbuf.h"
    #include "string2.h"

    @@ -1490,6 +1491,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;
    }
    @@ -1503,6 +1506,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;
    --
    2.42.0.655.g421f12c284-goog
    \
     
     \ /
      Last update: 2023-10-12 06:03    [W:4.355 / U:0.036 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site