From 222e0fcd06f3c5dd47f83985b5f9c598bed76427 Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Mon, 14 Apr 2025 15:25:48 +0200 Subject: [PATCH] debug: coredump: support dumping stack top only Add Kconfig option to dump only a portion of stack from the current stack pointer to the stack end. This is enough to let gdb reconstruct the stack trace and can significantly reduce the dump size. This is crucial if the core dump needs to be sent over radio. Additionally, add another option to set the limit for the dumped stack portion. Signed-off-by: Damian Krolik --- arch/Kconfig | 4 +++ arch/arm/core/cortex_m/coredump.c | 6 ++++ doc/releases/release-notes-4.2.rst | 6 ++++ kernel/include/kernel_arch_interface.h | 5 +++ subsys/debug/coredump/Kconfig | 23 +++++++++++++ subsys/debug/coredump/coredump_core.c | 45 ++++++++++++++++++++++---- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index eae4128fe98..b88935b7d06 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -33,6 +33,7 @@ config ARM select ARCH_IS_SET select ARCH_SUPPORTS_COREDUMP if CPU_CORTEX_M select ARCH_SUPPORTS_COREDUMP_THREADS if CPU_CORTEX_M + select ARCH_SUPPORTS_COREDUMP_STACK_PTR if CPU_CORTEX_M # FIXME: current state of the code for all ARM requires this, but # is really only necessary for Cortex-M with ARM MPU! select GEN_PRIV_STACKS @@ -705,6 +706,9 @@ config ARCH_SUPPORTS_COREDUMP_THREADS config ARCH_SUPPORTS_COREDUMP_PRIV_STACKS bool +config ARCH_SUPPORTS_COREDUMP_STACK_PTR + bool + config ARCH_SUPPORTS_ARCH_HW_INIT bool diff --git a/arch/arm/core/cortex_m/coredump.c b/arch/arm/core/cortex_m/coredump.c index ddb539c4e34..787cfcdf4a7 100644 --- a/arch/arm/core/cortex_m/coredump.c +++ b/arch/arm/core/cortex_m/coredump.c @@ -6,6 +6,7 @@ #include #include +#include #define ARCH_HDR_VER 2 @@ -96,3 +97,8 @@ uint16_t arch_coredump_tgt_code_get(void) { return COREDUMP_TGT_ARM_CORTEX_M; } + +uintptr_t arch_coredump_stack_ptr_get(const struct k_thread *thread) +{ + return (thread == _current) ? z_arm_coredump_fault_sp : thread->callee_saved.psp; +} diff --git a/doc/releases/release-notes-4.2.rst b/doc/releases/release-notes-4.2.rst index 595d5ce3239..daba10a2cce 100644 --- a/doc/releases/release-notes-4.2.rst +++ b/doc/releases/release-notes-4.2.rst @@ -226,6 +226,12 @@ New APIs and options * :kconfig:option:`CONFIG_NVME_PRP_PAGE_SIZE` +* Debug + + * Core Dump + + * :kconfig:option:`CONFIG_DEBUG_COREDUMP_THREAD_STACK_TOP`, enabled by default for ARM Cortex M when :kconfig:option:`CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN` is selected. + * Other * :kconfig:option:`CONFIG_LV_Z_COLOR_MONO_HW_INVERSION` diff --git a/kernel/include/kernel_arch_interface.h b/kernel/include/kernel_arch_interface.h index 05629c26a5f..956856a2ffc 100644 --- a/kernel/include/kernel_arch_interface.h +++ b/kernel/include/kernel_arch_interface.h @@ -638,6 +638,11 @@ void arch_coredump_info_dump(const struct arch_esf *esf); */ uint16_t arch_coredump_tgt_code_get(void); +/** + * @brief Get the stack pointer of the thread. + */ +uintptr_t arch_coredump_stack_ptr_get(const struct k_thread *thread); + #if defined(CONFIG_USERSPACE) || defined(__DOXYGEN__) /** diff --git a/subsys/debug/coredump/Kconfig b/subsys/debug/coredump/Kconfig index fdab0361820..1e763e522e1 100644 --- a/subsys/debug/coredump/Kconfig +++ b/subsys/debug/coredump/Kconfig @@ -144,4 +144,27 @@ module = DEBUG_COREDUMP module-str = coredump source "subsys/logging/Kconfig.template.log_config" +config DEBUG_COREDUMP_THREAD_STACK_TOP + bool "Dump top of stack only" + default y if DEBUG_COREDUMP_MEMORY_DUMP_MIN + depends on DEBUG_COREDUMP_MEMORY_DUMP_MIN || \ + DEBUG_COREDUMP_MEMORY_DUMP_THREADS + depends on ARCH_SUPPORTS_COREDUMP_STACK_PTR + help + This option enables dumping only the top portion of each thread's + stack, rather than the entire stack region. The top of the stack is + defined as the area from the stack pointer to the stack end, but the + size of this region can additionally be constrained using the + DEBUG_COREDUMP_THREAD_STACK_TOP_LIMIT option. + +config DEBUG_COREDUMP_THREAD_STACK_TOP_LIMIT + int "Stack top size limit" + default -1 + depends on DEBUG_COREDUMP_THREAD_STACK_TOP + help + See the description of the DEBUG_COREDUMP_THREAD_STACK_TOP option. + The value -1 indicates that there is no limit, meaning that the stack + is dumped till the end of its region. + + endif # DEBUG_COREDUMP diff --git a/subsys/debug/coredump/coredump_core.c b/subsys/debug/coredump/coredump_core.c index 12fa0fa8dfd..fc56e4cf3b6 100644 --- a/subsys/debug/coredump/coredump_core.c +++ b/subsys/debug/coredump/coredump_core.c @@ -41,6 +41,13 @@ static struct coredump_backend_api #define DT_DRV_COMPAT zephyr_coredump #endif +#if defined(CONFIG_DEBUG_COREDUMP_THREAD_STACK_TOP_LIMIT) && \ + CONFIG_DEBUG_COREDUMP_THREAD_STACK_TOP_LIMIT > 0 +#define STACK_TOP_LIMIT ((size_t)CONFIG_DEBUG_COREDUMP_THREAD_STACK_TOP_LIMIT) +#else +#define STACK_TOP_LIMIT SIZE_MAX +#endif + #if defined(CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK) __weak void arch_coredump_priv_stack_dump(struct k_thread *thread) { @@ -70,10 +77,35 @@ static void dump_header(unsigned int reason) backend_api->buffer_output((uint8_t *)&hdr, sizeof(hdr)); } -#if defined(CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN) || \ +#if defined(CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN) || \ defined(CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_THREADS) + +static inline void select_stack_region(const struct k_thread *thread, uintptr_t *start, + uintptr_t *end) +{ + uintptr_t sp; + + *start = thread->stack_info.start; + *end = thread->stack_info.start + thread->stack_info.size; + + if (!IS_ENABLED(CONFIG_DEBUG_COREDUMP_THREAD_STACK_TOP)) { + return; + } + + sp = arch_coredump_stack_ptr_get(thread); + + if (IN_RANGE(sp, *start, *end)) { + /* Skip ahead to the stack pointer. */ + *start = sp; + } + + /* Make sure no more than STACK_TOP_LIMIT bytes of the stack are dumped. */ + *end = *start + MIN((size_t)(*end - *start), STACK_TOP_LIMIT); +} + static void dump_thread(struct k_thread *thread) { + uintptr_t start_addr; uintptr_t end_addr; /* @@ -86,13 +118,12 @@ static void dump_thread(struct k_thread *thread) return; } - end_addr = POINTER_TO_UINT(thread) + sizeof(*thread); + start_addr = POINTER_TO_UINT(thread); + end_addr = start_addr + sizeof(*thread); + coredump_memory_dump(start_addr, end_addr); - coredump_memory_dump(POINTER_TO_UINT(thread), end_addr); - - end_addr = thread->stack_info.start + thread->stack_info.size; - - coredump_memory_dump(thread->stack_info.start, end_addr); + select_stack_region(thread, &start_addr, &end_addr); + coredump_memory_dump(start_addr, end_addr); #if defined(CONFIG_DEBUG_COREDUMP_DUMP_THREAD_PRIV_STACK) if ((thread->base.user_options & K_USER) == K_USER) {