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 <damian.krolik@nordicsemi.no>
This commit is contained in:
Damian Krolik 2025-04-14 15:25:48 +02:00 committed by Benjamin Cabé
commit 222e0fcd06
6 changed files with 82 additions and 7 deletions

View file

@ -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

View file

@ -6,6 +6,7 @@
#include <string.h>
#include <zephyr/debug/coredump.h>
#include <zephyr/kernel/thread.h>
#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;
}

View file

@ -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`

View file

@ -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__)
/**

View file

@ -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

View file

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