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

perf dwarf-aux: Add die_find_variable_by_reg() helper



The die_find_variable_by_reg() will search for a variable or a parameter
sub-DIE in the given scope DIE where the location matches to the given
register.

For the simplest and most common case, memory access usually happens
with a base register and an offset to the field so the register holds a
pointer in a variable or function parameter.  Then we can find one if it
has a location expression at the (instruction) address.  This function
only handles such a simple case for now.

In this case, the expression has a DW_OP_regN operation where N < 32.
If the register index (N) is greater than or equal to 32, DW_OP_regx
operation with an operand which saves the value for the N would be used.
It rejects expressions with more operations.

Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Acked-by: default avatarMasami 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-8-namhyung@kernel.org


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 981620fd
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -1245,6 +1245,73 @@ int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
out:
	return ret;
}

/* Interval parameters for __die_find_var_reg_cb() */
struct find_var_data {
	/* Target instruction address */
	Dwarf_Addr pc;
	/* Target register */
	unsigned reg;
};

/* Max number of registers DW_OP_regN supports */
#define DWARF_OP_DIRECT_REGS  32

/* Only checks direct child DIEs in the given scope. */
static int __die_find_var_reg_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_Op *ops;
	size_t nops;

	if (tag != DW_TAG_variable && tag != DW_TAG_formal_parameter)
		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) {
		/* Assuming the location list is sorted by address */
		if (end < data->pc)
			continue;
		if (start > data->pc)
			break;

		/* Only match with a simple case */
		if (data->reg < DWARF_OP_DIRECT_REGS) {
			if (ops->atom == (DW_OP_reg0 + data->reg) && nops == 1)
				return DIE_FIND_CB_END;
		} else {
			if (ops->atom == DW_OP_regx && ops->number == data->reg &&
			    nops == 1)
				return DIE_FIND_CB_END;
		}
	}
	return DIE_FIND_CB_SIBLING;
}

/**
 * die_find_variable_by_reg - Find a variable saved in a register
 * @sc_die: a scope DIE
 * @pc: the program address to find
 * @reg: the register number to find
 * @die_mem: a buffer to save the resulting DIE
 *
 * Find the variable DIE accessed by the given register.
 */
Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
				    Dwarf_Die *die_mem)
{
	struct find_var_data data = {
		.pc = pc,
		.reg = reg,
	};
	return die_find_child(sc_die, __die_find_var_reg_cb, &data, die_mem);
}
#endif

/*
+12 −0
Original line number Diff line number Diff line
@@ -137,6 +137,10 @@ int die_get_scopes(Dwarf_Die *cu_die, Dwarf_Addr pc, Dwarf_Die **scopes);
/* Get byte offset range of given variable DIE */
int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf);

/* Find a variable saved in the 'reg' at given address */
Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die, Dwarf_Addr pc, int reg,
				    Dwarf_Die *die_mem);

#else /*  HAVE_DWARF_GETLOCATIONS_SUPPORT */

static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
@@ -146,6 +150,14 @@ static inline int die_get_var_range(Dwarf_Die *sp_die __maybe_unused,
	return -ENOTSUP;
}

static inline Dwarf_Die *die_find_variable_by_reg(Dwarf_Die *sc_die __maybe_unused,
						  Dwarf_Addr pc __maybe_unused,
						  int reg __maybe_unused,
						  Dwarf_Die *die_mem __maybe_unused)
{
	return NULL;
}

#endif /* HAVE_DWARF_GETLOCATIONS_SUPPORT */

#endif /* _DWARF_AUX_H */