mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
alpha: add HAVE_PERF_REGS and HAVE_PERF_USER_STACK_DUMP support
Let perf sample registers and dump the user stack, which is what the perf tool needs for --call-graph dwarf. The register indices follow the hardware numbering, so r0-r30 map directly to DWARF register numbers, with pc after them. r9-r15 are callee-saved and live in switch_stack rather than pt_regs, so they cannot be sampled. Without r15 an unwinder cannot recover the CFA of a first frame that uses $15 as its frame pointer. sp is not in pt_regs either: for user frames it comes from user_stack_pointer(), and for kernel frames (PERF_SAMPLE_REGS_INTR) it is the end of the trap frame, as in stacktrace.c. Software events such as context switches and tracepoints hand perf a per-CPU pt_regs filled in by perf_fetch_caller_regs() instead of a trap frame. It is not on the stack and has no room for sp, so report sp as 0 there rather than the address of the per-CPU buffer. Assisted-by: Claude:claude-opus-5-5 Acked-by: Ian Rogers <irogers@google.com> Tested-by: Magnus Lindholm <linmag7@gmail.com> Reviewed-by: Magnus Lindholm <linmag7@gmail.com> Signed-off-by: Matt Turner <mattst88@gmail.com> Link: https://lore.kernel.org/r/20261018-alpha-perf-regs-v3-v3-4-52be85332c57@gmail.com Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
This commit is contained in:
parent
c5280a56dc
commit
9280e99d87
|
|
@ -6,7 +6,7 @@
|
||||||
-----------------------
|
-----------------------
|
||||||
| arch |status|
|
| arch |status|
|
||||||
-----------------------
|
-----------------------
|
||||||
| alpha: | TODO |
|
| alpha: | ok |
|
||||||
| arc: | TODO |
|
| arc: | TODO |
|
||||||
| arm: | ok |
|
| arm: | ok |
|
||||||
| arm64: | ok |
|
| arm64: | ok |
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
-----------------------
|
-----------------------
|
||||||
| arch |status|
|
| arch |status|
|
||||||
-----------------------
|
-----------------------
|
||||||
| alpha: | TODO |
|
| alpha: | ok |
|
||||||
| arc: | TODO |
|
| arc: | TODO |
|
||||||
| arm: | ok |
|
| arm: | ok |
|
||||||
| arm64: | ok |
|
| arm64: | ok |
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ config ALPHA
|
||||||
select HAVE_PAGE_SIZE_8KB
|
select HAVE_PAGE_SIZE_8KB
|
||||||
select HAVE_PCSPKR_PLATFORM
|
select HAVE_PCSPKR_PLATFORM
|
||||||
select HAVE_PERF_EVENTS
|
select HAVE_PERF_EVENTS
|
||||||
|
select HAVE_PERF_REGS
|
||||||
|
select HAVE_PERF_USER_STACK_DUMP
|
||||||
select NEED_DMA_MAP_STATE
|
select NEED_DMA_MAP_STATE
|
||||||
select NEED_SG_DMA_LENGTH
|
select NEED_SG_DMA_LENGTH
|
||||||
select GENERIC_IRQ_PROBE
|
select GENERIC_IRQ_PROBE
|
||||||
|
|
|
||||||
45
arch/alpha/include/uapi/asm/perf_regs.h
Normal file
45
arch/alpha/include/uapi/asm/perf_regs.h
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
|
#ifndef _ASM_ALPHA_PERF_REGS_H
|
||||||
|
#define _ASM_ALPHA_PERF_REGS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indices 0-30 match the hardware (and DWARF) register numbers.
|
||||||
|
* r9-r15 are not saved in pt_regs and cannot be sampled.
|
||||||
|
*/
|
||||||
|
enum perf_event_alpha_regs {
|
||||||
|
PERF_REG_ALPHA_R0,
|
||||||
|
PERF_REG_ALPHA_R1,
|
||||||
|
PERF_REG_ALPHA_R2,
|
||||||
|
PERF_REG_ALPHA_R3,
|
||||||
|
PERF_REG_ALPHA_R4,
|
||||||
|
PERF_REG_ALPHA_R5,
|
||||||
|
PERF_REG_ALPHA_R6,
|
||||||
|
PERF_REG_ALPHA_R7,
|
||||||
|
PERF_REG_ALPHA_R8,
|
||||||
|
PERF_REG_ALPHA_R9,
|
||||||
|
PERF_REG_ALPHA_R10,
|
||||||
|
PERF_REG_ALPHA_R11,
|
||||||
|
PERF_REG_ALPHA_R12,
|
||||||
|
PERF_REG_ALPHA_R13,
|
||||||
|
PERF_REG_ALPHA_R14,
|
||||||
|
PERF_REG_ALPHA_R15,
|
||||||
|
PERF_REG_ALPHA_R16,
|
||||||
|
PERF_REG_ALPHA_R17,
|
||||||
|
PERF_REG_ALPHA_R18,
|
||||||
|
PERF_REG_ALPHA_R19,
|
||||||
|
PERF_REG_ALPHA_R20,
|
||||||
|
PERF_REG_ALPHA_R21,
|
||||||
|
PERF_REG_ALPHA_R22,
|
||||||
|
PERF_REG_ALPHA_R23,
|
||||||
|
PERF_REG_ALPHA_R24,
|
||||||
|
PERF_REG_ALPHA_R25,
|
||||||
|
PERF_REG_ALPHA_R26,
|
||||||
|
PERF_REG_ALPHA_R27,
|
||||||
|
PERF_REG_ALPHA_R28,
|
||||||
|
PERF_REG_ALPHA_GP,
|
||||||
|
PERF_REG_ALPHA_SP,
|
||||||
|
PERF_REG_ALPHA_PC,
|
||||||
|
PERF_REG_ALPHA_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _ASM_ALPHA_PERF_REGS_H */
|
||||||
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
|
||||||
obj-$(CONFIG_SRM_ENV) += srm_env.o
|
obj-$(CONFIG_SRM_ENV) += srm_env.o
|
||||||
obj-$(CONFIG_MODULES) += module.o
|
obj-$(CONFIG_MODULES) += module.o
|
||||||
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
|
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
|
||||||
|
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
|
||||||
obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o
|
obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o
|
||||||
obj-$(CONFIG_AUDIT) += audit.o
|
obj-$(CONFIG_AUDIT) += audit.o
|
||||||
|
|
||||||
|
|
|
||||||
64
arch/alpha/kernel/perf_regs.c
Normal file
64
arch/alpha/kernel/perf_regs.c
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/perf_event.h>
|
||||||
|
#include <linux/sched/task_stack.h>
|
||||||
|
#include <asm/perf_regs.h>
|
||||||
|
|
||||||
|
#define REG(idx, field) \
|
||||||
|
[PERF_REG_ALPHA_##idx] = offsetof(struct pt_regs, field)
|
||||||
|
|
||||||
|
#define REG_RESERVED (~GENMASK_ULL(PERF_REG_ALPHA_MAX - 1, 0) | \
|
||||||
|
GENMASK_ULL(PERF_REG_ALPHA_R15, PERF_REG_ALPHA_R9))
|
||||||
|
|
||||||
|
static const unsigned int pt_regs_offset[PERF_REG_ALPHA_MAX] = {
|
||||||
|
REG(R0, r0), REG(R1, r1), REG(R2, r2), REG(R3, r3),
|
||||||
|
REG(R4, r4), REG(R5, r5), REG(R6, r6), REG(R7, r7),
|
||||||
|
REG(R8, r8), REG(R16, r16), REG(R17, r17), REG(R18, r18),
|
||||||
|
REG(R19, r19), REG(R20, r20), REG(R21, r21), REG(R22, r22),
|
||||||
|
REG(R23, r23), REG(R24, r24), REG(R25, r25), REG(R26, r26),
|
||||||
|
REG(R27, r27), REG(R28, r28), REG(GP, gp), REG(PC, pc),
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 perf_reg_value(struct pt_regs *regs, int idx)
|
||||||
|
{
|
||||||
|
if (idx == PERF_REG_ALPHA_SP) {
|
||||||
|
if (user_mode(regs))
|
||||||
|
return user_stack_pointer(regs);
|
||||||
|
/*
|
||||||
|
* Software events built by perf_fetch_caller_regs() use a
|
||||||
|
* per-CPU pt_regs rather than a trap frame, and pt_regs has
|
||||||
|
* nowhere to record sp, so report none.
|
||||||
|
*/
|
||||||
|
if (!object_is_on_stack(regs))
|
||||||
|
return 0;
|
||||||
|
/* Same kernel sp approximation as stacktrace.c. */
|
||||||
|
return (unsigned long)(regs + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WARN_ON_ONCE((u32)idx >= PERF_REG_ALPHA_MAX ||
|
||||||
|
BIT_ULL(idx) & REG_RESERVED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return *(unsigned long *)((void *)regs + pt_regs_offset[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_reg_validate(u64 mask)
|
||||||
|
{
|
||||||
|
if (!mask || mask & REG_RESERVED)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 perf_reg_abi(struct task_struct *task)
|
||||||
|
{
|
||||||
|
return PERF_SAMPLE_REGS_ABI_64;
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_get_regs_user(struct perf_regs *regs_user,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
regs_user->regs = task_pt_regs(current);
|
||||||
|
regs_user->abi = perf_reg_abi(current);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user