[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20240824111456.289367-1-dolinux.peng@gmail.com>
Date: Sat, 24 Aug 2024 04:14:56 -0700
From: Donglin Peng <dolinux.peng@...il.com>
To: catalin.marinas@....com
Cc: mark.rutland@....com,
will@...nel.org,
wangkefeng.wang@...wei.com,
ryan.roberts@....com,
linux-arm-kernel@...ts.infradead.org,
linux-kernel@...r.kernel.org,
Donglin Peng <dolinux.peng@...il.com>
Subject: [PATCH] arm64: Trace the memory abort exception
Introduce an event to trace the occurrence of memory abort exceptions.
The event can be useful if you only want to trace the page fault
exception when accessing a specific virtual address range, and it can
also help in locating the userspace code that triggers the fault.
I have referred to the trace_page_fault_user/trace_page_fault_kernel
on the x86 platform.
For example:
$ sudo perf record -e exceptions:mem_abort_user \
--filter 'address >= 0xffff8fe00010 && address <= 0xffff90e00010' \
-g -p 596 -- sleep 10
$ sudo perf script
fault 596 [000] 1218.765001: exceptions:mem_abort_user: address=0xffff9010e010 ip=0xaaaad8c90a6c error_code=0x9200000b
ffff800080028e78 do_mem_abort+0xd0 ([kernel.kallsyms])
ffff800080028e78 do_mem_abort+0xd0 ([kernel.kallsyms])
ffff800080c89a38 el0_da+0x38 ([kernel.kallsyms])
ffff800080c8ac54 el0t_64_sync_handler+0x8c ([kernel.kallsyms])
ffff80008001148c el0t_64_sync+0x14c ([kernel.kallsyms])
aaaad8c90a6c func_three+0xf0 (/home/pengdl/demo/pagefault/fault)
aaaad8c90ae4 func_two+0x20 (/home/pengdl/demo/pagefault/fault)
aaaad8c90b0c func_one+0x20 (/home/pengdl/demo/pagefault/fault)
aaaad8c90c54 main+0x140 (/home/pengdl/demo/pagefault/fault)
ffff90fece10 __libc_start_main+0xe8 (/usr/lib/aarch64-linux-gnu/libc-2.31.so)
aaaad8c908a4 _start+0x34 (/home/pengdl/demo/pagefault/fault)
Signed-off-by: Donglin Peng <dolinux.peng@...il.com>
---
arch/arm64/include/asm/trace/common.h | 18 +++++++
arch/arm64/include/asm/trace/exceptions.h | 59 +++++++++++++++++++++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/tracepoint.c | 23 +++++++++
arch/arm64/mm/fault.c | 18 +++++++
5 files changed, 119 insertions(+)
create mode 100644 arch/arm64/include/asm/trace/common.h
create mode 100644 arch/arm64/include/asm/trace/exceptions.h
create mode 100644 arch/arm64/kernel/tracepoint.c
diff --git a/arch/arm64/include/asm/trace/common.h b/arch/arm64/include/asm/trace/common.h
new file mode 100644
index 000000000000..a3efa45e2f20
--- /dev/null
+++ b/arch/arm64/include/asm/trace/common.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Based on arch/x86/include/asm/trace/common.h
+ *
+ */
+
+#ifndef _ASM_TRACE_COMMON_H
+#define _ASM_TRACE_COMMON_H
+
+#ifdef CONFIG_TRACING
+DECLARE_STATIC_KEY_FALSE(trace_memabort_key);
+#define trace_memabort_enabled() \
+ static_branch_unlikely(&trace_memabort_key)
+#else
+static inline bool trace_memabort_enabled(void) { return false; }
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/trace/exceptions.h b/arch/arm64/include/asm/trace/exceptions.h
new file mode 100644
index 000000000000..919400c7682d
--- /dev/null
+++ b/arch/arm64/include/asm/trace/exceptions.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Based on arch/x86/include/asm/trace/exceptions.h
+ *
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM exceptions
+
+#if !defined(_TRACE_MEM_ABORT_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MEM_ABORT_H
+
+#include <linux/tracepoint.h>
+#include <asm/trace/common.h>
+
+extern int trace_memabort_reg(void);
+extern void trace_memabort_unreg(void);
+
+DECLARE_EVENT_CLASS(arm64_exceptions,
+
+ TP_PROTO(unsigned long address, struct pt_regs *regs,
+ unsigned long error_code),
+
+ TP_ARGS(address, regs, error_code),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, address )
+ __field( unsigned long, ip )
+ __field( unsigned long, error_code )
+ ),
+
+ TP_fast_assign(
+ __entry->address = address;
+ __entry->ip = regs->pc;
+ __entry->error_code = error_code;
+ ),
+
+ TP_printk("address=%ps ip=%ps error_code=0x%lx",
+ (void *)__entry->address, (void *)__entry->ip,
+ __entry->error_code) );
+
+#define DEFINE_MEM_ABORT_EVENT(name) \
+DEFINE_EVENT_FN(arm64_exceptions, name, \
+ TP_PROTO(unsigned long address, struct pt_regs *regs, \
+ unsigned long error_code), \
+ TP_ARGS(address, regs, error_code), \
+ trace_memabort_reg, trace_memabort_unreg);
+
+DEFINE_MEM_ABORT_EVENT(mem_abort_user);
+DEFINE_MEM_ABORT_EVENT(mem_abort_kernel);
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH asm/trace
+#define TRACE_INCLUDE_FILE exceptions
+#endif /* _TRACE_MEM_ABORT_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 2b112f3b7510..f78aa49b8587 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_KUSER_HELPERS) += kuser32.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
obj-$(CONFIG_MODULES) += module.o module-plts.o
obj-$(CONFIG_PERF_EVENTS) += perf_regs.o perf_callchain.o
+obj-$(CONFIG_TRACING) += tracepoint.o
obj-$(CONFIG_HARDLOCKUP_DETECTOR_PERF) += watchdog_hld.o
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
diff --git a/arch/arm64/kernel/tracepoint.c b/arch/arm64/kernel/tracepoint.c
new file mode 100644
index 000000000000..c322e3644f75
--- /dev/null
+++ b/arch/arm64/kernel/tracepoint.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Based on arch/x86/kernel/tracepoint.c
+ *
+ */
+
+#include <linux/jump_label.h>
+#include <linux/atomic.h>
+
+#include <asm/trace/exceptions.h>
+
+DEFINE_STATIC_KEY_FALSE(trace_memabort_key);
+
+int trace_memabort_reg(void)
+{
+ static_branch_inc(&trace_memabort_key);
+ return 0;
+}
+
+void trace_memabort_unreg(void)
+{
+ static_branch_dec(&trace_memabort_key);
+}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 451ba7cbd5ad..aaccccb831a6 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -43,6 +43,9 @@
#include <asm/tlbflush.h>
#include <asm/traps.h>
+#define CREATE_TRACE_POINTS
+#include <asm/trace/exceptions.h>
+
struct fault_info {
int (*fn)(unsigned long far, unsigned long esr,
struct pt_regs *regs);
@@ -818,11 +821,26 @@ static const struct fault_info fault_info[] = {
{ do_bad, SIGKILL, SI_KERNEL, "unknown 63" },
};
+static __always_inline void
+trace_mem_abort_entries(struct pt_regs *regs, unsigned long error_code,
+ unsigned long address)
+{
+ if (!trace_memabort_enabled())
+ return;
+
+ if (user_mode(regs))
+ trace_mem_abort_user(address, regs, error_code);
+ else
+ trace_mem_abort_kernel(address, regs, error_code);
+}
+
void do_mem_abort(unsigned long far, unsigned long esr, struct pt_regs *regs)
{
const struct fault_info *inf = esr_to_fault_info(esr);
unsigned long addr = untagged_addr(far);
+ trace_mem_abort_entries(regs, esr, addr);
+
if (!inf->fn(far, esr, regs))
return;
--
2.25.1
Powered by blists - more mailing lists