perf annotate: Add annotate_get_insn_location()

The annotate_get_insn_location() is to get the detailed information of
instruction locations like registers and offset.  It has source and
target operands locations in an array.  Each operand can have a register
and an offset.  The offset is meaningful when mem_ref flag is set.

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>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231213001323.718046-7-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2023-12-12 16:13:12 -08:00 committed by Arnaldo Carvalho de Melo
parent 0669729eb0
commit 3a0c26edc3
2 changed files with 143 additions and 0 deletions

View file

@ -31,6 +31,7 @@
#include "bpf-utils.h"
#include "block-range.h"
#include "string2.h"
#include "dwarf-regs.h"
#include "util/event.h"
#include "util/sharded_mutex.h"
#include "arch/common.h"
@ -3518,3 +3519,109 @@ int annotate_check_args(void)
}
return 0;
}
/*
* Get register number and access offset from the given instruction.
* It assumes AT&T x86 asm format like OFFSET(REG). Maybe it needs
* to revisit the format when it handles different architecture.
* Fills @reg and @offset when return 0.
*/
static int extract_reg_offset(struct arch *arch, const char *str,
struct annotated_op_loc *op_loc)
{
char *p;
char *regname;
if (arch->objdump.register_char == 0)
return -1;
/*
* It should start from offset, but it's possible to skip 0
* in the asm. So 0(%rax) should be same as (%rax).
*
* However, it also start with a segment select register like
* %gs:0x18(%rbx). In that case it should skip the part.
*/
if (*str == arch->objdump.register_char) {
while (*str && !isdigit(*str) &&
*str != arch->objdump.memory_ref_char)
str++;
}
op_loc->offset = strtol(str, &p, 0);
p = strchr(p, arch->objdump.register_char);
if (p == NULL)
return -1;
regname = strdup(p);
if (regname == NULL)
return -1;
op_loc->reg = get_dwarf_regnum(regname, 0);
free(regname);
return 0;
}
/**
* annotate_get_insn_location - Get location of instruction
* @arch: the architecture info
* @dl: the target instruction
* @loc: a buffer to save the data
*
* 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.
*
* Some examples on x86:
*
* mov (%rax), %rcx # src_reg = rax, src_mem = 1, src_offset = 0
* # dst_reg = rcx, dst_mem = 0
*
* mov 0x18, %r8 # src_reg = -1, dst_reg = r8
*/
int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
struct annotated_insn_loc *loc)
{
struct ins_operands *ops;
struct annotated_op_loc *op_loc;
int i;
if (!strcmp(dl->ins.name, "lock"))
ops = dl->ops.locked.ops;
else
ops = &dl->ops;
if (ops == NULL)
return -1;
memset(loc, 0, sizeof(*loc));
for_each_insn_op_loc(loc, i, op_loc) {
const char *insn_str = ops->source.raw;
if (i == INSN_OP_TARGET)
insn_str = ops->target.raw;
/* Invalidate the register by default */
op_loc->reg = -1;
if (insn_str == NULL)
continue;
if (strchr(insn_str, arch->objdump.memory_ref_char)) {
op_loc->mem_ref = true;
extract_reg_offset(arch, insn_str, op_loc);
} else {
char *s = strdup(insn_str);
if (s) {
op_loc->reg = get_dwarf_regnum(s, 0);
free(s);
}
}
}
return 0;
}

View file

@ -439,4 +439,40 @@ int annotate_parse_percent_type(const struct option *opt, const char *_str,
int annotate_check_args(void);
/**
* struct annotated_op_loc - Location info of instruction operand
* @reg: Register in the operand
* @offset: Memory access offset in the operand
* @mem_ref: Whether the operand accesses memory
*/
struct annotated_op_loc {
int reg;
int offset;
bool mem_ref;
};
enum annotated_insn_ops {
INSN_OP_SOURCE = 0,
INSN_OP_TARGET = 1,
INSN_OP_MAX,
};
/**
* struct annotated_insn_loc - Location info of instruction
* @ops: Array of location info for source and target operands
*/
struct annotated_insn_loc {
struct annotated_op_loc ops[INSN_OP_MAX];
};
#define for_each_insn_op_loc(insn_loc, i, op_loc) \
for (i = INSN_OP_SOURCE, op_loc = &(insn_loc)->ops[i]; \
i < INSN_OP_MAX; \
i++, op_loc++)
/* Get detailed location info in the instruction */
int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl,
struct annotated_insn_loc *loc);
#endif /* __PERF_ANNOTATE_H */