Skip to content

Commit

Permalink
perf annotate-data: Handle array style accesses
Browse files Browse the repository at this point in the history
On x86, instructions for array access often looks like below.

  mov  0x1234(%rax,%rbx,8), %rcx

Usually the first register holds the type information and the second one
has the index.  And the current code only looks up a variable for the
first register.  But it's possible to be in the other way around so it
needs to check the second register if the first one failed.

The stat changed like this.

  Annotate data type stats:
  total 294, ok 148 (50.3%), bad 146 (49.7%)
  -----------------------------------------------------------
          30 : no_sym
          32 : no_mem_ops
          66 : no_var
          10 : no_typeinfo
           8 : bad_offset

Reviewed-by: Ian Rogers <[email protected]>
Cc: Stephane Eranian <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Namhyung Kim <[email protected]>
  • Loading branch information
namhyung committed Jan 22, 2024
1 parent 1cf4df0 commit d303019
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 19 deletions.
24 changes: 18 additions & 6 deletions tools/perf/util/annotate-data.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <stdlib.h>
#include <inttypes.h>

#include "annotate.h"
#include "annotate-data.h"
#include "debuginfo.h"
#include "debug.h"
Expand Down Expand Up @@ -207,7 +208,8 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset)
* It expects a pointer type for a memory access.
* Convert to a real type it points to.
*/
if (dwarf_tag(type_die) != DW_TAG_pointer_type ||
if ((dwarf_tag(type_die) != DW_TAG_pointer_type &&
dwarf_tag(type_die) != DW_TAG_array_type) ||
die_get_real_type(type_die, type_die) == NULL) {
pr_debug("no pointer or no type\n");
ann_data_stat.no_typeinfo++;
Expand All @@ -233,10 +235,11 @@ static int check_variable(Dwarf_Die *var_die, Dwarf_Die *type_die, int offset)

/* The result will be saved in @type_die */
static int find_data_type_die(struct debuginfo *di, u64 pc,
int reg, int offset, Dwarf_Die *type_die)
struct annotated_op_loc *loc, Dwarf_Die *type_die)
{
Dwarf_Die cu_die, var_die;
Dwarf_Die *scopes = NULL;
int reg, offset;
int ret = -1;
int i, nr_scopes;

Expand All @@ -250,6 +253,10 @@ static int find_data_type_die(struct debuginfo *di, u64 pc,
/* Get a list of nested scopes - i.e. (inlined) functions and blocks. */
nr_scopes = die_get_scopes(&cu_die, pc, &scopes);

reg = loc->reg1;
offset = loc->offset;

retry:
/* Search from the inner-most scope to the outer */
for (i = nr_scopes - 1; i >= 0; i--) {
/* Look up variables/parameters in this scope */
Expand All @@ -260,6 +267,12 @@ static int find_data_type_die(struct debuginfo *di, u64 pc,
ret = check_variable(&var_die, type_die, offset);
goto out;
}

if (loc->multi_regs && reg == loc->reg1 && loc->reg1 != loc->reg2) {
reg = loc->reg2;
goto retry;
}

if (ret < 0)
ann_data_stat.no_var++;

Expand All @@ -272,15 +285,14 @@ static int find_data_type_die(struct debuginfo *di, u64 pc,
* find_data_type - Return a data type at the location
* @ms: map and symbol at the location
* @ip: instruction address of the memory access
* @reg: register that holds the base address
* @offset: offset from the base address
* @loc: instruction operand location
*
* This functions searches the debug information of the binary to get the data
* type it accesses. The exact location is expressed by (ip, reg, offset).
* It return %NULL if not found.
*/
struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
int reg, int offset)
struct annotated_op_loc *loc)
{
struct annotated_data_type *result = NULL;
struct dso *dso = map__dso(ms->map);
Expand All @@ -300,7 +312,7 @@ struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
* a file address for DWARF processing.
*/
pc = map__rip_2objdump(ms->map, ip);
if (find_data_type_die(di, pc, reg, offset, &type_die) < 0)
if (find_data_type_die(di, pc, loc, &type_die) < 0)
goto out;

result = dso__findnew_data_type(dso, &type_die);
Expand Down
5 changes: 3 additions & 2 deletions tools/perf/util/annotate-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <linux/rbtree.h>
#include <linux/types.h>

struct annotated_op_loc;
struct evsel;
struct map_symbol;

Expand Down Expand Up @@ -105,7 +106,7 @@ extern struct annotated_data_stat ann_data_stat;

/* Returns data type at the location (ip, reg, offset) */
struct annotated_data_type *find_data_type(struct map_symbol *ms, u64 ip,
int reg, int offset);
struct annotated_op_loc *loc);

/* Update type access histogram at the given offset */
int annotated_data_type__update_samples(struct annotated_data_type *adt,
Expand All @@ -119,7 +120,7 @@ void annotated_data_type__tree_delete(struct rb_root *root);

static inline struct annotated_data_type *
find_data_type(struct map_symbol *ms __maybe_unused, u64 ip __maybe_unused,
int reg __maybe_unused, int offset __maybe_unused)
struct annotated_op_loc *loc __maybe_unused)
{
return NULL;
}
Expand Down
43 changes: 34 additions & 9 deletions tools/perf/util/annotate.c
Original file line number Diff line number Diff line change
Expand Up @@ -3563,8 +3563,22 @@ static int extract_reg_offset(struct arch *arch, const char *str,
if (regname == NULL)
return -1;

op_loc->reg = get_dwarf_regnum(regname, 0);
op_loc->reg1 = get_dwarf_regnum(regname, 0);
free(regname);

/* Get the second register */
if (op_loc->multi_regs) {
p = strchr(p + 1, arch->objdump.register_char);
if (p == NULL)
return -1;

regname = strdup(p);
if (regname == NULL)
return -1;

op_loc->reg2 = get_dwarf_regnum(regname, 0);
free(regname);
}
return 0;
}

Expand All @@ -3577,14 +3591,20 @@ static int extract_reg_offset(struct arch *arch, const char *str,
* Get detailed location info (register and offset) in the instruction.
* It needs both source and target operand and whether it accesses a
* memory location. The offset field is meaningful only when the
* corresponding mem flag is set.
* corresponding mem flag is set. The reg2 field is meaningful only
* when multi_regs flag is set.
*
* Some examples on x86:
*
* mov (%rax), %rcx # src_reg = rax, src_mem = 1, src_offset = 0
* # dst_reg = rcx, dst_mem = 0
* mov (%rax), %rcx # src_reg1 = rax, src_mem = 1, src_offset = 0
* # dst_reg1 = rcx, dst_mem = 0
*
* mov 0x18, %r8 # src_reg = -1, dst_reg = r8
* mov 0x18, %r8 # src_reg1 = -1, src_mem = 0
* # dst_reg1 = r8, dst_mem = 0
*
* mov %rsi, 8(%rbx,%rcx,4) # src_reg1 = rsi, src_mem = 0, dst_multi_regs = 0
* # dst_reg1 = rbx, dst_reg2 = rcx, dst_mem = 1
* # dst_multi_regs = 1, dst_offset = 8
*/
int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
struct annotated_insn_loc *loc)
Expand All @@ -3605,24 +3625,29 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,

for_each_insn_op_loc(loc, i, op_loc) {
const char *insn_str = ops->source.raw;
bool multi_regs = ops->source.multi_regs;

if (i == INSN_OP_TARGET)
if (i == INSN_OP_TARGET) {
insn_str = ops->target.raw;
multi_regs = ops->target.multi_regs;
}

/* Invalidate the register by default */
op_loc->reg = -1;
op_loc->reg1 = -1;
op_loc->reg2 = -1;

if (insn_str == NULL)
continue;

if (strchr(insn_str, arch->objdump.memory_ref_char)) {
op_loc->mem_ref = true;
op_loc->multi_regs = multi_regs;
extract_reg_offset(arch, insn_str, op_loc);
} else {
char *s = strdup(insn_str);

if (s) {
op_loc->reg = get_dwarf_regnum(s, 0);
op_loc->reg1 = get_dwarf_regnum(s, 0);
free(s);
}
}
Expand Down Expand Up @@ -3771,7 +3796,7 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
/* Recalculate IP because of LOCK prefix or insn fusion */
ip = ms->sym->start + dl->al.offset;

mem_type = find_data_type(ms, ip, op_loc->reg, op_loc->offset);
mem_type = find_data_type(ms, ip, op_loc);
if (mem_type)
istat->good++;
else
Expand Down
8 changes: 6 additions & 2 deletions tools/perf/util/annotate.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,14 +442,18 @@ int annotate_check_args(void);

/**
* struct annotated_op_loc - Location info of instruction operand
* @reg: Register in the operand
* @reg1: First register in the operand
* @reg2: Second register in the operand
* @offset: Memory access offset in the operand
* @mem_ref: Whether the operand accesses memory
* @multi_regs: Whether the second register is used
*/
struct annotated_op_loc {
int reg;
int reg1;
int reg2;
int offset;
bool mem_ref;
bool multi_regs;
};

enum annotated_insn_ops {
Expand Down

0 comments on commit d303019

Please sign in to comment.