Commit d3ab52c3 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

perf dwarf-regs: Add get_dwarf_regnum_for_perf_regnum() and use for x86 unwinding



Add a utility to map a perf register number to a DWARF register number
for a particular ELF machine type.

Create a generic unwind-libdw initial register initialization routine
that uses this function and thereby avoids arch specific
initialization. The unwind-libdw code does:

1) compute the maximum DWARF register from the set of sampled user registers,
2) allocates a set of DWARF registers,
3) copies the sample registers into the appropriate DWARF registers.

This generic solution is initially implemented for use with x86 as
only get_dwarf_regnum_for_perf_regnum() support for x86 is currently present.

Signed-off-by: default avatarIan Rogers <irogers@google.com>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent c3104008
Loading
Loading
Loading
Loading
+95 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <string.h> /* for strcmp */
#include <linux/kernel.h> /* for ARRAY_SIZE */
#include <dwarf-regs.h>
#include "../../../arch/x86/include/uapi/asm/perf_regs.h"

struct dwarf_regs_idx {
	const char *name;
@@ -163,3 +164,97 @@ int __get_dwarf_regnum_x86_64(const char *name)
{
	return get_regnum(x86_64_regidx_table, ARRAY_SIZE(x86_64_regidx_table), name);
}

int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum)
{
	static const int dwarf_i386_regnums[] = {
		[PERF_REG_X86_AX] = 0,
		[PERF_REG_X86_BX] = 3,
		[PERF_REG_X86_CX] = 1,
		[PERF_REG_X86_DX] = 2,
		[PERF_REG_X86_SI] = 6,
		[PERF_REG_X86_DI] = 7,
		[PERF_REG_X86_BP] = 5,
		[PERF_REG_X86_SP] = 4,
		[PERF_REG_X86_IP] = 8,
		[PERF_REG_X86_FLAGS] = 9,
		[PERF_REG_X86_CS] = 41,
		[PERF_REG_X86_SS] = 42,
		[PERF_REG_X86_DS] = 43,
		[PERF_REG_X86_ES] = 40,
		[PERF_REG_X86_FS] = 44,
		[PERF_REG_X86_GS] = 45,
		[PERF_REG_X86_XMM0] = 21,
		[PERF_REG_X86_XMM1] = 22,
		[PERF_REG_X86_XMM2] = 23,
		[PERF_REG_X86_XMM3] = 24,
		[PERF_REG_X86_XMM4] = 25,
		[PERF_REG_X86_XMM5] = 26,
		[PERF_REG_X86_XMM6] = 27,
		[PERF_REG_X86_XMM7] = 28,
	};

	if (perf_regnum == 0)
		return 0;

	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_i386_regnums) ||
	    dwarf_i386_regnums[perf_regnum] == 0)
		return -ENOENT;

	return dwarf_i386_regnums[perf_regnum];
}

int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum)
{
	static const int dwarf_x86_64_regnums[] = {
		[PERF_REG_X86_AX] = 0,
		[PERF_REG_X86_BX] = 3,
		[PERF_REG_X86_CX] = 2,
		[PERF_REG_X86_DX] = 1,
		[PERF_REG_X86_SI] = 4,
		[PERF_REG_X86_DI] = 5,
		[PERF_REG_X86_BP] = 6,
		[PERF_REG_X86_SP] = 7,
		[PERF_REG_X86_IP] = 16,
		[PERF_REG_X86_FLAGS] = 49,
		[PERF_REG_X86_CS] = 51,
		[PERF_REG_X86_SS] = 52,
		[PERF_REG_X86_DS] = 53,
		[PERF_REG_X86_ES] = 50,
		[PERF_REG_X86_FS] = 54,
		[PERF_REG_X86_GS] = 55,
		[PERF_REG_X86_R8] = 8,
		[PERF_REG_X86_R9] = 9,
		[PERF_REG_X86_R10] = 10,
		[PERF_REG_X86_R11] = 11,
		[PERF_REG_X86_R12] = 12,
		[PERF_REG_X86_R13] = 13,
		[PERF_REG_X86_R14] = 14,
		[PERF_REG_X86_R15] = 15,
		[PERF_REG_X86_XMM0] = 17,
		[PERF_REG_X86_XMM1] = 18,
		[PERF_REG_X86_XMM2] = 19,
		[PERF_REG_X86_XMM3] = 20,
		[PERF_REG_X86_XMM4] = 21,
		[PERF_REG_X86_XMM5] = 22,
		[PERF_REG_X86_XMM6] = 23,
		[PERF_REG_X86_XMM7] = 24,
		[PERF_REG_X86_XMM8] = 25,
		[PERF_REG_X86_XMM9] = 26,
		[PERF_REG_X86_XMM10] = 27,
		[PERF_REG_X86_XMM11] = 28,
		[PERF_REG_X86_XMM12] = 29,
		[PERF_REG_X86_XMM13] = 30,
		[PERF_REG_X86_XMM14] = 31,
		[PERF_REG_X86_XMM15] = 32,
	};

	if (perf_regnum == 0)
		return 0;

	if (perf_regnum <  0 || perf_regnum > (int)ARRAY_SIZE(dwarf_x86_64_regnums) ||
	    dwarf_x86_64_regnums[perf_regnum] == 0)
		return -ENOENT;

	return dwarf_x86_64_regnums[perf_regnum];
}
+55 −0
Original line number Diff line number Diff line
@@ -103,3 +103,58 @@ int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags
	free(regname);
	return reg;
}

static int get_libdw_frame_nregs(unsigned int machine, unsigned int flags __maybe_unused)
{
	switch (machine) {
	case EM_X86_64:
		return 17;
	case EM_386:
		return 9;
	case EM_ARM:
		return 16;
	case EM_AARCH64:
		return 97;
	case EM_CSKY:
		return 38;
	case EM_S390:
		return 32;
	case EM_PPC:
	case EM_PPC64:
		return 145;
	case EM_RISCV:
		return 66;
	case EM_SPARC:
	case EM_SPARCV9:
		return 103;
	case EM_LOONGARCH:
		return 74;
	default:
		return 0;
	}
}

int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine,
				     unsigned int flags, bool only_libdw_supported)
{
	int reg;

	switch (machine) {
	case EM_X86_64:
		reg = __get_dwarf_regnum_for_perf_regnum_x86_64(perf_regnum);
		break;
	case EM_386:
		reg = __get_dwarf_regnum_for_perf_regnum_i386(perf_regnum);
		break;
	default:
		pr_err("ELF MACHINE %x is not supported.\n", machine);
		return -ENOENT;
	}
	if (reg >= 0 && only_libdw_supported) {
		int nregs = get_libdw_frame_nregs(machine, flags);

		if (reg >= nregs)
			reg = -ENOENT;
	}
	return reg;
}
+8 −0
Original line number Diff line number Diff line
@@ -101,6 +101,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine, unsigned int

int __get_dwarf_regnum_i386(const char *name);
int __get_dwarf_regnum_x86_64(const char *name);
int __get_dwarf_regnum_for_perf_regnum_i386(int perf_regnum);
int __get_dwarf_regnum_for_perf_regnum_x86_64(int perf_regnum);

/*
 * get_dwarf_regnum - Returns DWARF regnum from register name
@@ -109,6 +111,12 @@ int __get_dwarf_regnum_x86_64(const char *name);
 */
int get_dwarf_regnum(const char *name, unsigned int machine, unsigned int flags);

/*
 * get_dwarf_regnum - Returns DWARF regnum from perf register number.
 */
int get_dwarf_regnum_for_perf_regnum(int perf_regnum, unsigned int machine, unsigned int flags,
				     bool only_libdw_supported);

void get_powerpc_regs(u32 raw_insn, int is_source, struct annotated_op_loc *op_loc);

#else /* HAVE_LIBDW_SUPPORT */
+0 −1
Original line number Diff line number Diff line
perf-util-y += unwind-libdw-x86.o
perf-util-y += unwind-libdw-arm.o
perf-util-y += unwind-libdw-arm64.o
perf-util-y += unwind-libdw-csky.o
+0 −54
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#include <elfutils/libdwfl.h>
#include "../arch/x86/include/uapi/asm/perf_regs.h"
#include "util/unwind-libdw.h"
#include "util/perf_regs.h"
#include "util/sample.h"

bool libdw_set_initial_registers_x86(Dwfl_Thread *thread, void *arg)
{
	struct unwind_info *ui = arg;
	struct regs_dump *user_regs = perf_sample__user_regs(ui->sample);
	Dwarf_Word dwarf_regs[17];
	unsigned nregs;

#define REG(r) ({						\
	Dwarf_Word val = 0;					\
	perf_reg_value(&val, user_regs, PERF_REG_X86_##r);	\
	val;							\
})

	if (user_regs->abi == PERF_SAMPLE_REGS_ABI_32) {
		dwarf_regs[0] = REG(AX);
		dwarf_regs[1] = REG(CX);
		dwarf_regs[2] = REG(DX);
		dwarf_regs[3] = REG(BX);
		dwarf_regs[4] = REG(SP);
		dwarf_regs[5] = REG(BP);
		dwarf_regs[6] = REG(SI);
		dwarf_regs[7] = REG(DI);
		dwarf_regs[8] = REG(IP);
		nregs = 9;
	} else {
		dwarf_regs[0]  = REG(AX);
		dwarf_regs[1]  = REG(DX);
		dwarf_regs[2]  = REG(CX);
		dwarf_regs[3]  = REG(BX);
		dwarf_regs[4]  = REG(SI);
		dwarf_regs[5]  = REG(DI);
		dwarf_regs[6]  = REG(BP);
		dwarf_regs[7]  = REG(SP);
		dwarf_regs[8]  = REG(R8);
		dwarf_regs[9]  = REG(R9);
		dwarf_regs[10] = REG(R10);
		dwarf_regs[11] = REG(R11);
		dwarf_regs[12] = REG(R12);
		dwarf_regs[13] = REG(R13);
		dwarf_regs[14] = REG(R14);
		dwarf_regs[15] = REG(R15);
		dwarf_regs[16] = REG(IP);
		nregs = 17;
	}

	return dwfl_thread_state_registers(thread, 0, nregs, dwarf_regs);
}
Loading