Commit 3a0c26ed authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

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: 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>
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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 0669729e
Loading
Loading
Loading
Loading
+107 −0
Original line number Diff line number Diff line
@@ -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;
}
+36 −0
Original line number Diff line number Diff line
@@ -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 */