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) {