coredump: Add config for capturing thread data in core dump

Update core dump file format to support a new section which contains
metadata about threads necessary for debugging.

Define configs to capture that metadata and include it in the dumps
when enabled.

Update documentation to reflect the changes.

Signed-off-by: Mark Holden <mholden@meta.com>
This commit is contained in:
Mark Holden 2024-06-24 13:23:48 -07:00 committed by Anas Nashif
commit 45684a598d
4 changed files with 111 additions and 4 deletions

View file

@ -30,6 +30,13 @@ Here are the choices regarding memory dump:
walking the stack in the debugger. Use this only if absolute minimum of data
dump is desired.
* ``DEBUG_COREDUMP_MEMORY_DUMP_THREADS``: Dumps the thread struct and stack of all
threads and all data required to debug threads.
* ``DEBUG_COREDUMP_MEMORY_DUMP_LINKER_RAM``: Dumps the memory region between
_image_ram_start[] and _image_ram_end[]. This includes at least data, noinit,
and BSS sections. This is the default.
Additional memory can be included in a dump (even with the "DEBUG_COREDUMP_MEMORY_DUMP_MIN"
config selected) through one or more :ref:`coredump devices <coredump_device_api>`
@ -244,7 +251,8 @@ File Format
***********
The core dump binary file consists of one file header, one
architecture-specific block, and multiple memory blocks. All numbers in
architecture-specific block, zero or one threads metadata block(s),
and multiple memory blocks. All numbers in
the headers below are little endian.
File Header
@ -315,6 +323,36 @@ to the target architecture (e.g. CPU registers)
- ``uint8_t[]``
- Contains target architecture specific data.
Threads Metadata Block
---------------------------
The threads metadata block contains the byte stream of data necessary
for debugging threads.
.. list-table:: Threads Metadata Block
:widths: 2 1 7
:header-rows: 1
* - Field
- Data Type
- Description
* - ID
- ``char``
- ``T`` to indicate this is a threads metadata block.
* - Header version
- ``uint16_t``
- Identify the version of the header. This needs to be incremented
whenever the header struct is modified. This allows parser to
reject older header versions so it will not incorrectly parse
the header.
* - Number of bytes
- ``uint16_t``
- Number of bytes following the header which contains the byte stream
for target data.
* - Byte stream
- ``uint8_t[]``
- Contains data necessary for debugging threads.
Memory Block
------------

View file

@ -142,10 +142,13 @@ struct coredump_cmd_copy_arg {
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/byteorder.h>
#define COREDUMP_HDR_VER 1
#define COREDUMP_HDR_VER 2
#define COREDUMP_ARCH_HDR_ID 'A'
#define THREADS_META_HDR_ID 'T'
#define THREADS_META_HDR_VER 1
#define COREDUMP_MEM_HDR_ID 'M'
#define COREDUMP_MEM_HDR_VER 1
@ -192,6 +195,18 @@ struct coredump_arch_hdr_t {
uint16_t num_bytes;
} __packed;
/* Threads metadata header */
struct coredump_threads_meta_hdr_t {
/* THREADS_META_HDR_ID */
char id;
/* Header version */
uint16_t hdr_version;
/* Number of bytes in this block (excluding header) */
uint16_t num_bytes;
} __packed;
/* Memory block header */
struct coredump_mem_hdr_t {
/* COREDUMP_MEM_HDR_ID */

View file

@ -58,6 +58,15 @@ config DEBUG_COREDUMP_MEMORY_DUMP_MIN
Don't use this unless you want absolutely
minimum core dump.
config DEBUG_COREDUMP_MEMORY_DUMP_THREADS
bool "Threads"
select THREAD_STACK_INFO
select DEBUG_THREAD_INFO
select DEBUG_COREDUMP_THREADS_METADATA
help
Dumps the thread struct and stack of all
threads and all data required to debug threads.
config DEBUG_COREDUMP_MEMORY_DUMP_LINKER_RAM
bool "RAM defined by linker section"
help
@ -87,4 +96,11 @@ config DEBUG_COREDUMP_SHELL
help
This shell provides access to coredump and its backends.
config DEBUG_COREDUMP_THREADS_METADATA
bool "Threads metadata"
select DEBUG_THREAD_INFO
help
Core dump will contain the threads metadata section containing
any necessary data to enable debugging threads
endif # DEBUG_COREDUMP

View file

@ -60,7 +60,6 @@ static void dump_header(unsigned int reason)
static void dump_thread(struct k_thread *thread)
{
#ifdef CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN
uintptr_t end_addr;
/*
@ -80,7 +79,6 @@ static void dump_thread(struct k_thread *thread)
end_addr = thread->stack_info.start + thread->stack_info.size;
coredump_memory_dump(thread->stack_info.start, end_addr);
#endif
}
#if defined(CONFIG_COREDUMP_DEVICE)
@ -111,12 +109,46 @@ void process_memory_region_list(void)
}
#endif
#ifdef CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_THREADS
/*
* Content of _kernel.threads not being modified during dump
* capture so no need to lock z_thread_monitor_lock.
*/
struct k_thread *current;
for (current = _kernel.threads; current; current = current->next_thread) {
dump_thread(current);
}
/* Also add interrupt stack, in case error occurred in an interrupt */
char *irq_stack = _kernel.cpus[0].irq_stack;
uintptr_t start_addr = POINTER_TO_UINT(irq_stack) - CONFIG_ISR_STACK_SIZE;
coredump_memory_dump(start_addr, POINTER_TO_UINT(irq_stack));
#endif /* CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_THREADS */
#if defined(CONFIG_COREDUMP_DEVICE)
#define MY_FN(inst) process_coredump_dev_memory(DEVICE_DT_INST_GET(inst));
DT_INST_FOREACH_STATUS_OKAY(MY_FN)
#endif
}
#ifdef CONFIG_DEBUG_COREDUMP_THREADS_METADATA
static void dump_threads_metadata(void)
{
struct coredump_threads_meta_hdr_t hdr = {
.id = THREADS_META_HDR_ID,
.hdr_version = THREADS_META_HDR_VER,
.num_bytes = 0,
};
hdr.num_bytes += sizeof(_kernel);
coredump_buffer_output((uint8_t *)&hdr, sizeof(hdr));
coredump_buffer_output((uint8_t *)&_kernel, sizeof(_kernel));
}
#endif /* CONFIG_DEBUG_COREDUMP_THREADS_METADATA */
void coredump(unsigned int reason, const struct arch_esf *esf,
struct k_thread *thread)
{
@ -128,8 +160,14 @@ void coredump(unsigned int reason, const struct arch_esf *esf,
arch_coredump_info_dump(esf);
}
#ifdef CONFIG_DEBUG_COREDUMP_THREADS_METADATA
dump_threads_metadata();
#endif
if (thread != NULL) {
#ifdef CONFIG_DEBUG_COREDUMP_MEMORY_DUMP_MIN
dump_thread(thread);
#endif
}
process_memory_region_list();