[PATCH v2 1/2] arm: process: dump memory around registers when displaying regs

From: Enric Balletbo i Serra
Date: Tue Apr 25 2017 - 09:44:56 EST


Dump a block of kernel memory from around the registers in the kernel oops
dump. This is behind a config option (DEBUG_AROUND_REGS), since it adds
a bunch of noise to the kernel logs (which someone can find valuable but
not everyone else will).

This is extremely useful in diagnosing remote crashes, and is based heavily
on original work by Michael Davidson <md@xxxxxxxxxx> and San Mehat
<san@xxxxxxxxxx>.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@xxxxxxxxxxxxx>
---
This is a second version of a patch that was initially sent by John Stultz
in 2010[1], current version is a rework trying to address the feedback
received in the first version. We're still carrying this patch on recent
chromeos kernels and find it useful. This is a second attempt to try
to get this acceptable, so waiting for your feedback, and I hope I'll be
able to do the modifications needed.

Changes since v1:
- Use dump_mem() to dump memory.
- Put the code behind a config option.

[1] http://www.spinics.net/lists/arm-kernel/msg107477.html
---
arch/arm/include/asm/traps.h | 1 +
arch/arm/kernel/process.c | 34 ++++++++++++++++++++++++++++++++++
arch/arm/kernel/traps.c | 6 ++----
lib/Kconfig.debug | 11 +++++++++++
4 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/arch/arm/include/asm/traps.h b/arch/arm/include/asm/traps.h
index f555bb3..24e65c2 100644
--- a/arch/arm/include/asm/traps.h
+++ b/arch/arm/include/asm/traps.h
@@ -47,6 +47,7 @@ static inline int in_exception_text(unsigned long ptr)
}

extern void __init early_trap_init(void *);
+extern void dump_mem(const char *lvl, const char *str, unsigned long bottom, unsigned long top);
extern void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame);
extern void ptrace_break(struct task_struct *tsk, struct pt_regs *regs);

diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 939e8b5..3894cf2 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -34,6 +34,7 @@

#include <asm/processor.h>
#include <asm/thread_notify.h>
+#include <asm/traps.h>
#include <asm/stacktrace.h>
#include <asm/system_misc.h>
#include <asm/mach/time.h>
@@ -94,6 +95,37 @@ void arch_cpu_idle_exit(void)
ledtrig_cpu(CPU_LED_IDLE_END);
}

+/*
+ * Dump a block of kernel memory from around the given address
+ */
+static void __show_regs_extra_data(struct pt_regs *regs, int nbytes)
+{
+#ifdef CONFIG_DEBUG_AROUND_REGS
+ struct map_regs {
+ unsigned long reg;
+ const char *name;
+ };
+ struct map_regs map_dump[] = {
+ { regs->ARM_pc, "PC " }, { regs->ARM_lr, "LR " },
+ { regs->ARM_sp, "SP " }, { regs->ARM_ip, "IP " },
+ { regs->ARM_fp, "FP " }, { regs->ARM_r0, "R0 " },
+ { regs->ARM_r1, "R1 " }, { regs->ARM_r2, "R2 " },
+ { regs->ARM_r3, "R3 " }, { regs->ARM_r4, "R4 " },
+ { regs->ARM_r5, "R5 " }, { regs->ARM_r6, "R6 " },
+ { regs->ARM_r7, "R7 " }, { regs->ARM_r8, "R8 " },
+ { regs->ARM_r9, "R9 " }, { regs->ARM_r10, "R10 " },
+ { -1, NULL },
+ };
+ struct map_regs *map = map_dump;
+
+ while (map->name) {
+ dump_mem("", map->name, map->reg - nbytes,
+ map->reg + nbytes);
+ map++;
+ }
+#endif
+}
+
void __show_regs(struct pt_regs *regs)
{
unsigned long flags;
@@ -185,6 +217,8 @@ void __show_regs(struct pt_regs *regs)
printk("Control: %08x%s\n", ctrl, buf);
}
#endif
+
+ __show_regs_extra_data(regs, 128);
}

void show_regs(struct pt_regs * regs)
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 948c648..f4ee05b 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -62,8 +62,6 @@ static int __init user_debug_setup(char *str)
__setup("user_debug=", user_debug_setup);
#endif

-static void dump_mem(const char *, const char *, unsigned long, unsigned long);
-
void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame)
{
#ifdef CONFIG_KALLSYMS
@@ -115,8 +113,8 @@ static int verify_stack(unsigned long sp)
/*
* Dump out the contents of some memory nicely...
*/
-static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
- unsigned long top)
+void dump_mem(const char *lvl, const char *str, unsigned long bottom,
+ unsigned long top)
{
unsigned long first;
mm_segment_t fs;
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 77fadfa..b72d4ce6 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -726,6 +726,17 @@ config DEBUG_STACKOVERFLOW

If in doubt, say "N".

+config DEBUG_AROUND_REGS
+ bool "Dump a block of kernel memory from around registers"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here to dump a block of kernel memory around the registers
+ in the kernel oops dump. This is useful in diagnosing remote
+ crashes.
+
+ Note that selecting this option will increase significally the
+ information in the oops dump. Most people should say N here.
+
source "lib/Kconfig.kmemcheck"

source "lib/Kconfig.kasan"
--
2.9.3