From nobody Thu Sep 24 14:26:26 2026 Received: from mx.itxnorge.no (itx-kvm-14.itxnorge.no [91.189.121.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A5EC4A3D41; Tue, 22 Sep 2026 20:15:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.189.121.228 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108138; cv=none; b=VivCtw+gIiiX4y1SwWXcrg4k9Tq66riazPp4qTKgBbGVJYsNNxc3JvYwL0pE9pyXl/1YlfssXWIVBU44DQfnkvMX+59qkuQurWKXLCNjNZ4IRtzIqVX05H29iw7EDvDXY6/LTp5VGbtwlw7N7z5jflZ07qo2ZWrmw6fTKTx9WvY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108138; c=relaxed/simple; bh=3C+io7Yq19YmBExI43YBPe/62sja9K3kdCDyE7hhx4Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=VCdJll0jh9SEa3mY08H1xdfJ0kn/urC2mCXFhXokC+eyWISLjAVRAlMYmtinkYHnyqLhIfouEPabzh5Eh1XhdOKTRWp6vJWd+NKXvLtZoJF9pcSgq1jTO9XdzhdoSLSoZkCupVwsmh6js6h1y8xBCSLoIK37F0/bhj87GdTbe3o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no; spf=pass smtp.mailfrom=itx.no; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b=eo7/i89U; arc=none smtp.client-ip=91.189.121.228 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=itx.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b="eo7/i89U" From: Stian Halseth DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=itx.no; s=mx.itx.no; t=1790108130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nwfjX7nuhFS4t6Nv0nmF2X/fHblO6tMBdc3Wj6hQel8=; b=eo7/i89UZ0HIz0CLDlU/oljgpvtONYBIGMWA3xV/H2IVX7AJ+x3Maw2VLTltYARLzJWM3J BXTbXnNAWi8ztANDCwa/ZTQk8DtFS6FAC+8hzF/V7UpEeBFkn9eOc8Qd0YRok4ADpJynbW Bor7nI2Qn6gn+EpKkikkA5idbW4l8d4= To: Peter Zijlstra , Ingo Molnar , Andreas Larsson , "David S. Miller" Cc: Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark , Jonathan Corbet , Shuah Khan , Randy Dunlap , Magnus Lindholm , linux-perf-users@vger.kernel.org, sparclinux@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Stian Halseth Subject: [RFC PATCH v2 1/3] perf/core: Let an arch prepare the user stack before it is dumped Date: Tue, 22 Sep 2026 22:15:05 +0200 Message-ID: <20260922201507.1719668-2-stian@itx.no> In-Reply-To: <20260922201507.1719668-1-stian@itx.no> References: <20260922201507.1719668-1-stian@itx.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" PERF_SAMPLE_STACK_USER copies the user stack as it is in memory, which assumes the whole call chain is there. On sparc the sampled window's %l and %i registers, including the frame pointer and return address an unwinder starts from, may still be in the register file: they only reach the stack when a window spills. perf_callchain_user() already handles this for the callchain by calling flushw_user() first; the user stack dump has no equivalent arch entry point. Add perf_arch_prepare_ustack(), a no-op by default, called from perf_prepare_sample() before the dump size is computed, so an arch can write back whatever part of the user's stack state it still holds in registers. Doing this in the arch PMU interrupt handler would not do: software events such as cpu-clock reach perf_event_overflow() without passing through it. Signed-off-by: Stian Halseth --- include/linux/perf_event.h | 7 +++++++ kernel/events/core.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 5842552294c1..758b98b75346 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1815,6 +1815,13 @@ extern unsigned long perf_instruction_pointer(struct= perf_event *event, #ifndef perf_arch_bpf_user_pt_regs # define perf_arch_bpf_user_pt_regs(regs) regs #endif +/* + * Called before the user stack of the current task is dumped, for an arch + * that still holds part of the user's stack state in registers. + */ +#ifndef perf_arch_prepare_ustack +static inline void perf_arch_prepare_ustack(void) { } +#endif =20 #ifndef perf_arch_guest_misc_flags static inline unsigned long perf_arch_guest_misc_flags(struct pt_regs *reg= s) diff --git a/kernel/events/core.c b/kernel/events/core.c index db7b76d6b68a..90fb35c7d279 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8718,6 +8718,8 @@ void perf_prepare_sample(struct perf_sample_data *dat= a, u16 header_size =3D perf_sample_data_size(data, event); u16 size =3D sizeof(u64); =20 + if (data->regs_user.regs) + perf_arch_prepare_ustack(); stack_size =3D perf_sample_ustack_size(stack_size, header_size, data->regs_user.regs); =20 --=20 2.55.0 From nobody Thu Sep 24 14:26:26 2026 Received: from mx.itxnorge.no (itx-kvm-14.itxnorge.no [91.189.121.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97FFA495029; Tue, 22 Sep 2026 20:15:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.189.121.228 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108140; cv=none; b=ffW0Wq2t5tdjKOLxRyKqsjDPH/v+m5Xd6JLDQCTefYq3WVrk88E1rY9AgzXxfo8mgxXgOrZlUy3qHK+AqUyg5sEeakvMN84JCmfPoKK8SNppHk2slj6FdkYHmf0yM7IMIAauK7jhwSi6jGKQolC7DyVLhC8jb4ap4y93l6fl73A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108140; c=relaxed/simple; bh=lhUx29EswnsXc0CtXm4KjouhRkqiGp3lYPwDGO9aLxQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=XnlAOYDiHSGE4QaKkVsQArXPbvS+R6qPI+RxhNkj08qDmOEZ7O0MTwaz7tMGb04My2aLiDZYAPQih8Iku+GEJEMzKJ4z/zGXfPfBVBtfnVZu6/yiXc0n5fZYS6t3pkQtuL36qH+5kcrlCni+yVYNqpvoPl2Gsxgo5Tv168muwE4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no; spf=pass smtp.mailfrom=itx.no; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b=L1jZey/1; arc=none smtp.client-ip=91.189.121.228 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=itx.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b="L1jZey/1" From: Stian Halseth DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=itx.no; s=mx.itx.no; t=1790108130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=gCjbmsJfQGQn+3BbHYLh/zVJrApu2LzN6m90GD2+p24=; b=L1jZey/1NvEWGM+zkTV8WXZIfYhdLFC/JwArpbHNOTGooQSvKP7MnEBN3a6cmXEWtwdeZx FmL4rOwPao73tSX+sZCjk9ArVN8QYHKsFiIMPTDcybrOC5D+Rmq0Edd+V8U0Plcz3/qAZd C18k850AqkFf7bH5rinru8wt/53JIYM= To: Peter Zijlstra , Ingo Molnar , Andreas Larsson , "David S. Miller" Cc: Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark , Jonathan Corbet , Shuah Khan , Randy Dunlap , Magnus Lindholm , linux-perf-users@vger.kernel.org, sparclinux@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Stian Halseth Subject: [RFC PATCH v2 2/3] sparc64: Support PERF_SAMPLE_REGS_USER and PERF_SAMPLE_STACK_USER Date: Tue, 22 Sep 2026 22:15:06 +0200 Message-ID: <20260922201507.1719668-3-stian@itx.no> In-Reply-To: <20260922201507.1719668-1-stian@itx.no> References: <20260922201507.1719668-1-stian@itx.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Select HAVE_PERF_REGS and HAVE_PERF_USER_STACK_DUMP and add the perf_regs implementation. The exposed registers mirror struct pt_regs: %g0-%g7, %o0-%o7 (named from the user's point of view; pt_regs calls them UREG_I*), %tstate, PC, NPC and %y. %o6 is reported as held in the register, i.e. with the 2047 stack bias for a 64-bit task, the same as ptrace and user_stack_pointer(). The stack dump therefore also starts at the biased address, as consumers of PERF_SAMPLE_STACK_USER expect the dump to begin at the sampled stack pointer value; the first 2047 bytes lie below the frame, and a sample whose %sp page is not mapped yet gets an empty dump, as on any arch with a not-present stack page. User-mode values of a 32-bit task are truncated to 32 bits as genregs32_get() does, since the hardware does not zero-extend them. Kernel-mode registers, as seen by PERF_SAMPLE_REGS_INTR, are left alone. perf_arch_prepare_ustack() flushes the user register windows so the sampled window's %l and %i registers are in the dumped stack, where a DWARF unwinder needs them: the CFI of a function after `save` defines the CFA in terms of %i6. This makes perf record --call-graph dwarf and eu-stackprof usable on sparc64. Signed-off-by: Stian Halseth --- .../features/perf/perf-regs/arch-support.txt | 2 +- .../perf/perf-stackdump/arch-support.txt | 2 +- arch/sparc/Kconfig | 2 + arch/sparc/include/asm/perf_event.h | 3 + arch/sparc/include/uapi/asm/perf_regs.h | 33 ++++++++++ arch/sparc/kernel/Makefile | 2 +- arch/sparc/kernel/perf_regs.c | 65 +++++++++++++++++++ 7 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 arch/sparc/include/uapi/asm/perf_regs.h create mode 100644 arch/sparc/kernel/perf_regs.c diff --git a/Documentation/features/perf/perf-regs/arch-support.txt b/Docum= entation/features/perf/perf-regs/arch-support.txt index 4c9a5a012075..41ab622efa15 100644 --- a/Documentation/features/perf/perf-regs/arch-support.txt +++ b/Documentation/features/perf/perf-regs/arch-support.txt @@ -23,7 +23,7 @@ | riscv: | ok | | s390: | ok | | sh: | TODO | - | sparc: | TODO | + | sparc: | ok | | um: | TODO | | x86: | ok | | xtensa: | TODO | diff --git a/Documentation/features/perf/perf-stackdump/arch-support.txt b/= Documentation/features/perf/perf-stackdump/arch-support.txt index dd362b5cb638..cafa6fae7704 100644 --- a/Documentation/features/perf/perf-stackdump/arch-support.txt +++ b/Documentation/features/perf/perf-stackdump/arch-support.txt @@ -23,7 +23,7 @@ | riscv: | ok | | s390: | ok | | sh: | TODO | - | sparc: | TODO | + | sparc: | ok | | um: | TODO | | x86: | ok | | xtensa: | TODO | diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index ab77d3f2536e..9d7f575f359d 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -93,6 +93,8 @@ config SPARC64 select RTC_DRV_SUN4V select RTC_DRV_STARFIRE select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select PERF_USE_VMALLOC select ARCH_HAVE_NMI_SAFE_CMPXCHG select HAVE_C_RECORDMCOUNT diff --git a/arch/sparc/include/asm/perf_event.h b/arch/sparc/include/asm/p= erf_event.h index c2aec0c7f4f5..03f1ab1618b9 100644 --- a/arch/sparc/include/asm/perf_event.h +++ b/arch/sparc/include/asm/perf_event.h @@ -25,6 +25,9 @@ do { \ (regs)->u_regs[UREG_I6] =3D _fp; \ (regs)->u_regs[UREG_I7] =3D _i7; \ } while (0) + +void perf_arch_prepare_ustack(void); +#define perf_arch_prepare_ustack perf_arch_prepare_ustack #endif =20 #endif diff --git a/arch/sparc/include/uapi/asm/perf_regs.h b/arch/sparc/include/u= api/asm/perf_regs.h new file mode 100644 index 000000000000..17f14335d6c0 --- /dev/null +++ b/arch/sparc/include/uapi/asm/perf_regs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_SPARC_PERF_REGS_H +#define _UAPI_ASM_SPARC_PERF_REGS_H + +/* + * Mirrors struct pt_regs. O6 is %sp as held in the register, i.e. biased + * by 2047 for a 64-bit task. + */ +enum perf_event_sparc_regs { + PERF_REG_SPARC_G0, + PERF_REG_SPARC_G1, + PERF_REG_SPARC_G2, + PERF_REG_SPARC_G3, + PERF_REG_SPARC_G4, + PERF_REG_SPARC_G5, + PERF_REG_SPARC_G6, + PERF_REG_SPARC_G7, + PERF_REG_SPARC_O0, + PERF_REG_SPARC_O1, + PERF_REG_SPARC_O2, + PERF_REG_SPARC_O3, + PERF_REG_SPARC_O4, + PERF_REG_SPARC_O5, + PERF_REG_SPARC_O6, + PERF_REG_SPARC_O7, + PERF_REG_SPARC_TSTATE, + PERF_REG_SPARC_PC, + PERF_REG_SPARC_NPC, + PERF_REG_SPARC_Y, + PERF_REG_SPARC_MAX +}; + +#endif /* _UAPI_ASM_SPARC_PERF_REGS_H */ diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 497b5714fa8f..a3dd92247c3b 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -113,7 +113,7 @@ obj-$(CONFIG_AUDIT) +=3D audit.o audit--$(CONFIG_AUDIT) :=3D compat_audit.o obj-$(CONFIG_COMPAT) +=3D $(audit--y) =20 -pc--$(CONFIG_PERF_EVENTS) :=3D perf_event.o +pc--$(CONFIG_PERF_EVENTS) :=3D perf_event.o perf_regs.o obj-$(CONFIG_SPARC64) +=3D $(pc--y) =20 obj-$(CONFIG_UPROBES) +=3D uprobes.o diff --git a/arch/sparc/kernel/perf_regs.c b/arch/sparc/kernel/perf_regs.c new file mode 100644 index 000000000000..726516abf5a7 --- /dev/null +++ b/arch/sparc/kernel/perf_regs.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + u64 val; + + switch (idx) { + case PERF_REG_SPARC_G0 ... PERF_REG_SPARC_O7: + val =3D regs->u_regs[idx]; + break; + case PERF_REG_SPARC_TSTATE: + return regs->tstate; + case PERF_REG_SPARC_PC: + val =3D regs->tpc; + break; + case PERF_REG_SPARC_NPC: + val =3D regs->tnpc; + break; + case PERF_REG_SPARC_Y: + return regs->y; + default: + WARN_ON_ONCE(1); + return 0; + } + + if (user_mode(regs) && test_thread_flag(TIF_32BIT)) + val =3D (u32)val; + + return val; +} + +#define REG_RESERVED (~((1ULL << PERF_REG_SPARC_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ + if (test_tsk_thread_flag(task, TIF_32BIT)) + return PERF_SAMPLE_REGS_ABI_32; + + return PERF_SAMPLE_REGS_ABI_64; +} + +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs) +{ + regs_user->regs =3D task_pt_regs(current); + regs_user->abi =3D perf_reg_abi(current); +} + +void perf_arch_prepare_ustack(void) +{ + flushw_user(); +} --=20 2.55.0 From nobody Thu Sep 24 14:26:26 2026 Received: from mx.itxnorge.no (itx-kvm-14.itxnorge.no [91.189.121.228]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 93F2D2222A9; Tue, 22 Sep 2026 20:15:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.189.121.228 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108154; cv=none; b=LnFe4caNhL0srx3mu8Qwvtk595Mb8tNqurumnNs6Mxs3XjQ5sSoZtviJZSfVwb6GrMNf1vl9Ab06+VEqa+HcCLUIs6dxECl4ET8F7pHYRcZGFRNpTAvkYYb45OBPUcWoMrX7UlTaoBewErLzBMOHgWBdaDCLXwWFFWRhby9uGHc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790108154; c=relaxed/simple; bh=5HwOkeajFExTgDT0Eialrh888c/hZqMgUaTrl96yZvQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MmLjjFlB3TIHxIxIC9TTcqwZjokJeRaq6Yh7yr+pIW7+utxQMiJyRZbV6AhIVTSqXlrWin3meJa0fzOJnkgZShavp6gnrganlN3fFd51O4NXt1BWZvsekZMLXyvH4kKgF1c0WnYrxKWPuMLhFqXgnQgdhbEst9j0XKdR+wfjmAY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no; spf=pass smtp.mailfrom=itx.no; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b=bpYWIRM4; arc=none smtp.client-ip=91.189.121.228 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=itx.no Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=itx.no Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=itx.no header.i=@itx.no header.b="bpYWIRM4" From: Stian Halseth DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=itx.no; s=mx.itx.no; t=1790108130; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4osou6eg55N84tmZ03BCHuM/vBlN+i7pY6GUjSE042U=; b=bpYWIRM4AUq54HniVgoH0RVkB3ujWcpzYrFJWX8fAwCV96jr5FMWtnB8xksfT6AqN2hHzC cya8sBYFnz8MHI4eI7wDKOpRcu3pjPzwzRBHi8Eo5AJLjYCnC6EIk+hXCcmWA+JpuNFlJ7 nZL/SzTsF2G0W4tuuwjif9jK6L6UeAc= To: Peter Zijlstra , Ingo Molnar , Andreas Larsson , "David S. Miller" Cc: Arnaldo Carvalho de Melo , Namhyung Kim , Mark Rutland , Alexander Shishkin , Jiri Olsa , Ian Rogers , Adrian Hunter , James Clark , Jonathan Corbet , Shuah Khan , Randy Dunlap , Magnus Lindholm , linux-perf-users@vger.kernel.org, sparclinux@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, Stian Halseth Subject: [RFC PATCH v2 3/3] perf tools: Support sparc user register samples and dwarf unwinding Date: Tue, 22 Sep 2026 22:15:07 +0200 Message-ID: <20260922201507.1719668-4-stian@itx.no> In-Reply-To: <20260922201507.1719668-1-stian@itx.no> References: <20260922201507.1719668-1-stian@itx.no> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the sparc perf_regs tables and the EM_SPARC/EM_SPARCV9 dispatch for register names, masks, ip/sp and the perf-to-DWARF register mapping, plus the uapi header copy. For libdw unwinding, seed DWARF registers 16-31 (%l0-%l7, %i0-%i7) from the register save area at the top of the stack dump: the kernel flushes the sampled window there, and the CFI after `save` needs %i6 and %i7 to find the first caller. The dump base on sparc is the biased %sp, so read words from the dump with memcpy rather than an aligned dereference. Signed-off-by: Stian Halseth --- tools/arch/sparc/include/uapi/asm/perf_regs.h | 33 +++++++++ tools/perf/arch/sparc/include/perf_regs.h | 18 +++++ tools/perf/check-headers.sh | 1 + tools/perf/util/dwarf-regs-arch/Build | 1 + .../util/dwarf-regs-arch/dwarf-regs-sparc.c | 12 ++++ tools/perf/util/dwarf-regs.c | 4 ++ tools/perf/util/include/dwarf-regs.h | 1 + tools/perf/util/perf-regs-arch/Build | 1 + .../util/perf-regs-arch/perf_regs_sparc.c | 69 +++++++++++++++++++ tools/perf/util/perf_regs.c | 18 +++++ tools/perf/util/perf_regs.h | 5 ++ tools/perf/util/unwind-libdw.c | 41 ++++++++++- 12 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 tools/arch/sparc/include/uapi/asm/perf_regs.h create mode 100644 tools/perf/arch/sparc/include/perf_regs.h create mode 100644 tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c create mode 100644 tools/perf/util/perf-regs-arch/perf_regs_sparc.c diff --git a/tools/arch/sparc/include/uapi/asm/perf_regs.h b/tools/arch/spa= rc/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000000..17f14335d6c0 --- /dev/null +++ b/tools/arch/sparc/include/uapi/asm/perf_regs.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_SPARC_PERF_REGS_H +#define _UAPI_ASM_SPARC_PERF_REGS_H + +/* + * Mirrors struct pt_regs. O6 is %sp as held in the register, i.e. biased + * by 2047 for a 64-bit task. + */ +enum perf_event_sparc_regs { + PERF_REG_SPARC_G0, + PERF_REG_SPARC_G1, + PERF_REG_SPARC_G2, + PERF_REG_SPARC_G3, + PERF_REG_SPARC_G4, + PERF_REG_SPARC_G5, + PERF_REG_SPARC_G6, + PERF_REG_SPARC_G7, + PERF_REG_SPARC_O0, + PERF_REG_SPARC_O1, + PERF_REG_SPARC_O2, + PERF_REG_SPARC_O3, + PERF_REG_SPARC_O4, + PERF_REG_SPARC_O5, + PERF_REG_SPARC_O6, + PERF_REG_SPARC_O7, + PERF_REG_SPARC_TSTATE, + PERF_REG_SPARC_PC, + PERF_REG_SPARC_NPC, + PERF_REG_SPARC_Y, + PERF_REG_SPARC_MAX +}; + +#endif /* _UAPI_ASM_SPARC_PERF_REGS_H */ diff --git a/tools/perf/arch/sparc/include/perf_regs.h b/tools/perf/arch/sp= arc/include/perf_regs.h new file mode 100644 index 000000000000..493fbff9ea16 --- /dev/null +++ b/tools/perf/arch/sparc/include/perf_regs.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_PERF_REGS_H +#define ARCH_PERF_REGS_H + +#include +#include +#include "../../../../arch/sparc/include/uapi/asm/perf_regs.h" + +#define PERF_REGS_MASK ((1ULL << PERF_REG_SPARC_MAX) - 1) +#define PERF_REGS_MAX PERF_REG_SPARC_MAX + +#ifdef __arch64__ +#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_64 +#else +#define PERF_SAMPLE_REGS_ABI PERF_SAMPLE_REGS_ABI_32 +#endif + +#endif /* ARCH_PERF_REGS_H */ diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 531c0e0e84df..f144590f5082 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -42,6 +42,7 @@ declare -a FILES=3D( "arch/mips/include/uapi/asm/perf_regs.h" "arch/powerpc/include/uapi/asm/perf_regs.h" "arch/s390/include/uapi/asm/perf_regs.h" + "arch/sparc/include/uapi/asm/perf_regs.h" "arch/x86/include/uapi/asm/perf_regs.h" "arch/x86/include/uapi/asm/kvm.h" "arch/x86/include/uapi/asm/svm.h" diff --git a/tools/perf/util/dwarf-regs-arch/Build b/tools/perf/util/dwarf-= regs-arch/Build index ceb68ae86fd8..bda944029046 100644 --- a/tools/perf/util/dwarf-regs-arch/Build +++ b/tools/perf/util/dwarf-regs-arch/Build @@ -6,4 +6,5 @@ perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-mips.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-powerpc.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-riscv.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-s390.o +perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-sparc.o perf-util-$(CONFIG_LIBDW) +=3D dwarf-regs-x86.o diff --git a/tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c b/tools/per= f/util/dwarf-regs-arch/dwarf-regs-sparc.c new file mode 100644 index 000000000000..dd0c80e29764 --- /dev/null +++ b/tools/perf/util/dwarf-regs-arch/dwarf-regs-sparc.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "../../../arch/sparc/include/uapi/asm/perf_regs.h" + +int __get_dwarf_regnum_for_perf_regnum_sparc(int perf_regnum) +{ + if (perf_regnum < PERF_REG_SPARC_G0 || perf_regnum > PERF_REG_SPARC_O7) + return -ENOENT; + + return perf_regnum; +} diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c index 797f455eba0d..df9200c31aaa 100644 --- a/tools/perf/util/dwarf-regs.c +++ b/tools/perf/util/dwarf-regs.c @@ -217,6 +217,10 @@ int get_dwarf_regnum_for_perf_regnum(int perf_regnum, = unsigned int machine, case EM_S390: reg =3D __get_dwarf_regnum_for_perf_regnum_s390(perf_regnum); break; + case EM_SPARC: + case EM_SPARCV9: + reg =3D __get_dwarf_regnum_for_perf_regnum_sparc(perf_regnum); + break; case EM_LOONGARCH: reg =3D __get_dwarf_regnum_for_perf_regnum_loongarch(perf_regnum); break; diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include= /dwarf-regs.h index 46a764cf322f..4a02f30c5604 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -113,6 +113,7 @@ int __get_dwarf_regnum_for_perf_regnum_loongarch(int pe= rf_regnum); int __get_dwarf_regnum_for_perf_regnum_powerpc(int perf_regnum); int __get_dwarf_regnum_for_perf_regnum_riscv(int perf_regnum); int __get_dwarf_regnum_for_perf_regnum_s390(int perf_regnum); +int __get_dwarf_regnum_for_perf_regnum_sparc(int perf_regnum); int __get_dwarf_regnum_for_perf_regnum_mips(int perf_regnum); =20 /* diff --git a/tools/perf/util/perf-regs-arch/Build b/tools/perf/util/perf-re= gs-arch/Build index be95402aa540..47220a2f2dae 100644 --- a/tools/perf/util/perf-regs-arch/Build +++ b/tools/perf/util/perf-regs-arch/Build @@ -6,4 +6,5 @@ perf-util-y +=3D perf_regs_mips.o perf-util-y +=3D perf_regs_powerpc.o perf-util-y +=3D perf_regs_riscv.o perf-util-y +=3D perf_regs_s390.o +perf-util-y +=3D perf_regs_sparc.o perf-util-y +=3D perf_regs_x86.o diff --git a/tools/perf/util/perf-regs-arch/perf_regs_sparc.c b/tools/perf/= util/perf-regs-arch/perf_regs_sparc.c new file mode 100644 index 000000000000..36d84ddd3a48 --- /dev/null +++ b/tools/perf/util/perf-regs-arch/perf_regs_sparc.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "../perf_regs.h" +#include "../../arch/sparc/include/perf_regs.h" + +uint64_t __perf_reg_mask_sparc(bool intr __maybe_unused) +{ + return PERF_REGS_MASK; +} + +const char *__perf_reg_name_sparc(int id) +{ + switch (id) { + case PERF_REG_SPARC_G0: + return "g0"; + case PERF_REG_SPARC_G1: + return "g1"; + case PERF_REG_SPARC_G2: + return "g2"; + case PERF_REG_SPARC_G3: + return "g3"; + case PERF_REG_SPARC_G4: + return "g4"; + case PERF_REG_SPARC_G5: + return "g5"; + case PERF_REG_SPARC_G6: + return "g6"; + case PERF_REG_SPARC_G7: + return "g7"; + case PERF_REG_SPARC_O0: + return "o0"; + case PERF_REG_SPARC_O1: + return "o1"; + case PERF_REG_SPARC_O2: + return "o2"; + case PERF_REG_SPARC_O3: + return "o3"; + case PERF_REG_SPARC_O4: + return "o4"; + case PERF_REG_SPARC_O5: + return "o5"; + case PERF_REG_SPARC_O6: + return "sp"; + case PERF_REG_SPARC_O7: + return "o7"; + case PERF_REG_SPARC_TSTATE: + return "tstate"; + case PERF_REG_SPARC_PC: + return "pc"; + case PERF_REG_SPARC_NPC: + return "npc"; + case PERF_REG_SPARC_Y: + return "y"; + default: + return NULL; + } + + return NULL; +} + +uint64_t __perf_reg_ip_sparc(void) +{ + return PERF_REG_SPARC_PC; +} + +uint64_t __perf_reg_sp_sparc(void) +{ + return PERF_REG_SPARC_O6; +} diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c index 558c143abbab..5880b54fd7dc 100644 --- a/tools/perf/util/perf_regs.c +++ b/tools/perf/util/perf_regs.c @@ -68,6 +68,10 @@ uint64_t perf_intr_reg_mask(uint16_t e_machine) case EM_S390: mask =3D __perf_reg_mask_s390(/*intr=3D*/true); break; + case EM_SPARC: + case EM_SPARCV9: + mask =3D __perf_reg_mask_sparc(/*intr=3D*/true); + break; case EM_386: case EM_X86_64: mask =3D __perf_reg_mask_x86(/*intr=3D*/true); @@ -111,6 +115,10 @@ uint64_t perf_user_reg_mask(uint16_t e_machine) case EM_S390: mask =3D __perf_reg_mask_s390(/*intr=3D*/false); break; + case EM_SPARC: + case EM_SPARCV9: + mask =3D __perf_reg_mask_sparc(/*intr=3D*/false); + break; case EM_386: case EM_X86_64: mask =3D __perf_reg_mask_x86(/*intr=3D*/false); @@ -154,6 +162,10 @@ const char *perf_reg_name(int id, uint16_t e_machine, = uint32_t e_flags) case EM_S390: reg_name =3D __perf_reg_name_s390(id); break; + case EM_SPARC: + case EM_SPARCV9: + reg_name =3D __perf_reg_name_sparc(id); + break; case EM_386: case EM_X86_64: reg_name =3D __perf_reg_name_x86(id); @@ -215,6 +227,9 @@ uint64_t perf_arch_reg_ip(uint16_t e_machine) return __perf_reg_ip_riscv(); case EM_S390: return __perf_reg_ip_s390(); + case EM_SPARC: + case EM_SPARCV9: + return __perf_reg_ip_sparc(); case EM_386: case EM_X86_64: return __perf_reg_ip_x86(); @@ -244,6 +259,9 @@ uint64_t perf_arch_reg_sp(uint16_t e_machine) return __perf_reg_sp_riscv(); case EM_S390: return __perf_reg_sp_s390(); + case EM_SPARC: + case EM_SPARCV9: + return __perf_reg_sp_sparc(); case EM_386: case EM_X86_64: return __perf_reg_sp_x86(); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 79be2b791509..3f453d910aa4 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -65,6 +65,11 @@ uint64_t __perf_reg_ip_s390(void); uint64_t __perf_reg_sp_s390(void); int __perf_sdt_arg_parse_op_s390(char *old_op, char **new_op); =20 +uint64_t __perf_reg_mask_sparc(bool intr); +const char *__perf_reg_name_sparc(int id); +uint64_t __perf_reg_ip_sparc(void); +uint64_t __perf_reg_sp_sparc(void); + int __perf_sdt_arg_parse_op_x86(char *old_op, char **new_op); uint64_t __perf_reg_mask_x86(bool intr); const char *__perf_reg_name_x86(int id); diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 63a5c2253174..62484bcd5cd3 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include "debug.h" #include "dso.h" #include @@ -261,12 +263,44 @@ static bool memory_read(Dwfl *dwfl __maybe_unused, Dw= arf_Addr addr, Dwarf_Word * } =20 offset =3D addr - start; - *result =3D *(Dwarf_Word *)&stack->data[offset]; + memcpy(result, &stack->data[offset], sizeof(*result)); pr_debug("unwind: access_mem addr 0x%" PRIx64 ", val %lx, offset %d\n", addr, (unsigned long)*result, offset); return true; } =20 +/* + * The sampled window's %l and %i registers (DWARF 16-31) are not in the + * sample; the kernel flushed them to the register save area at the top of + * the dumped stack. + */ +static void libdw_set_sparc_window_registers(struct unwind_info *ui, + struct regs_dump *user_regs, + Dwarf_Word *dwarf_regs) +{ + struct stack_dump *stack =3D &ui->sample->user_stack; + bool abi64 =3D user_regs->abi =3D=3D PERF_SAMPLE_REGS_ABI_64; + size_t wsize =3D abi64 ? sizeof(u64) : sizeof(u32); + u64 sp, offset =3D abi64 ? 2047 : 0; + + if (perf_reg_value(&sp, user_regs, perf_arch_reg_sp(ui->e_machine))) + return; + if (offset + 16 * wsize > stack->size) + return; + + for (int i =3D 0; i < 16; i++) { + const char *p =3D &stack->data[offset + i * wsize]; + u32 w32; + + if (abi64) { + memcpy(&dwarf_regs[16 + i], p, sizeof(u64)); + } else { + memcpy(&w32, p, sizeof(w32)); + dwarf_regs[16 + i] =3D w32; + } + } +} + static bool libdw_set_initial_registers(Dwfl_Thread *thread, void *arg) { struct dwfl_ui_thread_info *dwfl_ui_ti =3D arg; @@ -276,6 +310,7 @@ static bool libdw_set_initial_registers(Dwfl_Thread *th= read, void *arg) int max_dwarf_reg =3D 0; bool ret; uint16_t e_machine =3D ui->e_machine; + bool is_sparc =3D e_machine =3D=3D EM_SPARC || e_machine =3D=3D EM_SPARCV= 9; int e_flags =3D ui->e_flags; uint64_t ip_perf_reg =3D perf_arch_reg_ip(e_machine); Dwarf_Word val =3D 0; @@ -295,6 +330,8 @@ static bool libdw_set_initial_registers(Dwfl_Thread *th= read, void *arg) max_dwarf_reg =3D dwarf_reg; } } + if (is_sparc && max_dwarf_reg < 31) + max_dwarf_reg =3D 31; =20 dwarf_regs =3D calloc(max_dwarf_reg + 1, sizeof(*dwarf_regs)); if (!dwarf_regs) @@ -313,6 +350,8 @@ static bool libdw_set_initial_registers(Dwfl_Thread *th= read, void *arg) } } } + if (is_sparc) + libdw_set_sparc_window_registers(ui, user_regs, dwarf_regs); if (perf_reg_value(&val, user_regs, ip_perf_reg) =3D=3D 0) dwfl_thread_state_register_pc(thread, val); =20 --=20 2.55.0