diff mbox series

arm64: Trace the memory abort exception

Message ID 20240824111456.289367-1-dolinux.peng@gmail.com (mailing list archive)
State New, archived
Headers show
Series arm64: Trace the memory abort exception | expand

Commit Message

Donglin Peng Aug. 24, 2024, 11:14 a.m. UTC
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@gmail.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 mbox series

Patch

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;