perf dwarf-aux: Add die_find_variable_by_addr()

The die_find_variable_by_addr() is to find a variables in the given DIE
using given (PC-relative) address.  Global variables will have a
location expression with DW_OP_addr which has an address so can simply
compare it with the address.

  <1><143a7>: Abbrev Number: 2 (DW_TAG_variable)
      <143a8>   DW_AT_name        : loops_per_jiffy
      <143ac>   DW_AT_type        : <0x1cca>
      <143b0>   DW_AT_external    : 1
      <143b0>   DW_AT_decl_file   : 193
      <143b1>   DW_AT_decl_line   : 213
      <143b2>   DW_AT_location    : 9 byte block: 3 b0 46 41 82 ff ff ff ff
                                     (DW_OP_addr: ffffffff824146b0)

Note that the type-offset should be calculated from the base address of
the global variable.

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Andi Kleen <ak@linux.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: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231110000012.3538610-33-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2023-11-09 15:59:51 -08:00 committed by Arnaldo Carvalho de Melo
parent 72108c0b9c
commit d60469d7c0
2 changed files with 93 additions and 0 deletions

View File

@ -1250,8 +1250,12 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
struct find_var_data {
/* Target instruction address */
Dwarf_Addr pc;
/* Target memory address (for global data) */
Dwarf_Addr addr;
/* Target register */
unsigned reg;
/* Access offset, set for global data */
int offset;
};
/* Max number of registers DW_OP_regN supports */
@ -1312,6 +1316,81 @@ Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
};
return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
}
/* Only checks direct child DIEs in the given scope */
static int __die_find_var_addr_cb(Dwarf_Die *die_mem, void *arg)
{
struct find_var_data *data = arg;
int tag = dwarf_tag(die_mem);
ptrdiff_t off = 0;
Dwarf_Attribute attr;
Dwarf_Addr base, start, end;
Dwarf_Word size;
Dwarf_Die type_die;
Dwarf_Op *ops;
size_t nops;
if (tag != DW_TAG_variable)
return DIE_FIND_CB_SIBLING;
if (dwarf_attr(die_mem, DW_AT_location, &attr) == NULL)
return DIE_FIND_CB_SIBLING;
while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) {
if (ops->atom != DW_OP_addr)
continue;
if (data->addr < ops->number)
continue;
if (data->addr == ops->number) {
/* Update offset relative to the start of the variable */
data->offset = 0;
return DIE_FIND_CB_END;
}
if (die_get_real_type(die_mem, &type_die) == NULL)
continue;
if (dwarf_aggregate_size(&type_die, &size) < 0)
continue;
if (data->addr >= ops->number + size)
continue;
/* Update offset relative to the start of the variable */
data->offset = data->addr - ops->number;
return DIE_FIND_CB_END;
}
return DIE_FIND_CB_SIBLING;
}
/**
* die_find_variable_by_addr - Find variable located at given address
* @sc_die: a scope DIE
* @pc: the program address to find
* @addr: the data address to find
* @die_mem: a buffer to save the resulting DIE
* @offset: the offset in the resulting type
*
* Find the variable DIE located at the given address (in PC-relative mode).
* This is usually for global variables.
*/
Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr pc,
Dwarf_Addr addr, Dwarf_Die *die_mem,
int *offset)
{
struct find_var_data data = {
.pc = pc,
.addr = addr,
};
Dwarf_Die *result;
result = die_find_child(sc_die, __die_find_var_addr_cb, &data, die_mem);
if (result)
*offset = data.offset;
return result;
}
#endif
/*

View File

@ -141,6 +141,11 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);
Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
Dwarf_Die *die_mem);
/* Find a (global) variable located in the 'addr' */
Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die, Dwarf_Addr pc,
Dwarf_Addr addr, Dwarf_Die *die_mem,
int *offset);
#else /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
@ -158,6 +163,15 @@ static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unus
return NULL;
}
static inline Dwarf_Die *die_find_variable_by_addr(Dwarf_Die *sc_die __maybe_unused,
Dwarf_Addr pc __maybe_unused,
Dwarf_Addr addr __maybe_unused,
Dwarf_Die *die_mem __maybe_unused,
int *offset __maybe_unused)
{
return NULL;
}
#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */
#endif /* _DWARF_AUX_H */