From 35051dd7153eae6caa538789b99b5ea042e2bae5 Mon Sep 17 00:00:00 2001 From: anjiahao Date: Thu, 9 Nov 2023 12:02:37 +0800 Subject: [PATCH] coredump: support coredump save to block device when crash Signed-off-by: anjiahao --- Documentation/guides/coredump.rst | 2 +- boards/Kconfig | 23 +- .../sabre-6quad/configs/coredump/defconfig | 2 +- include/nuttx/binfmt/binfmt.h | 14 +- include/nuttx/memoryregion.h | 30 +++ libs/libc/misc/lib_memoryregion.c | 74 +++++- sched/init/nx_bringup.c | 6 + sched/misc/CMakeLists.txt | 4 + sched/misc/Make.defs | 4 + sched/misc/assert.c | 67 +---- sched/misc/coredump.c | 228 ++++++++++++++++++ sched/misc/coredump.h | 58 +++++ 12 files changed, 441 insertions(+), 71 deletions(-) create mode 100644 sched/misc/coredump.c create mode 100644 sched/misc/coredump.h diff --git a/Documentation/guides/coredump.rst b/Documentation/guides/coredump.rst index a231119198..94dfa7fc30 100644 --- a/Documentation/guides/coredump.rst +++ b/Documentation/guides/coredump.rst @@ -20,7 +20,7 @@ Enable Kconfig CONFIG_ELF_COREDUMP=y /* Enable ELF Coredump */ - CONFIG_BOARD_COREDUMP=y /* Enable Board Coredump, if exceptions and assertions occur, */ + CONFIG_BOARD_COREDUMP_SYSLOG=y /* Enable Board Coredump, if exceptions and assertions occur, */ CONFIG_SYSTEM_COREDUMP=y /* Enable coredump in user command, which can capture the current state of one or all threads when the system is running, the diff --git a/boards/Kconfig b/boards/Kconfig index c8448b3003..1c944deaf9 100644 --- a/boards/Kconfig +++ b/boards/Kconfig @@ -4366,18 +4366,30 @@ config BOARD_CRASHDUMP "machine state" in a place where on the next reset can write it to more sophisticated storage in a sane operating environment. -config BOARD_COREDUMP - bool "Enable Core Dump after assert" +config BOARD_COREDUMP_SYSLOG + bool "Enable Core dump to syslog" default n depends on ELF_COREDUMP ---help--- - Enable to support for the dump core information after assert. + Enable put coredump to syslog when crash. -if BOARD_COREDUMP +config BOARD_COREDUMP_BLKDEV + bool "Enable Core Dump to block device" + default n + depends on ELF_COREDUMP + ---help--- + Enable save coredump at block device when crash. + +config BOARD_COREDUMP_BLKDEV_PATH + string "Save Core Dump block device PATH" + depends on BOARD_COREDUMP_BLKDEV + ---help--- + Save coredump file block device path. config BOARD_COREDUMP_FULL bool "Core Dump all thread registers and stacks" default y + depends on BOARD_COREDUMP_SYSLOG || BOARD_COREDUMP_BLKDEV ---help--- Enable to support for the dump all task registers and stacks. @@ -4385,11 +4397,10 @@ config BOARD_COREDUMP_COMPRESSION bool "Enable Core Dump compression" default y select LIBC_LZF + depends on BOARD_COREDUMP_SYSLOG || BOARD_COREDUMP_BLKDEV ---help--- Enable LZF compression algorithm for core dump content -endif # BOARD_COREDUMP - config BOARD_ENTROPY_POOL bool "Enable Board level storing of entropy pool structure" default n diff --git a/boards/arm/imx6/sabre-6quad/configs/coredump/defconfig b/boards/arm/imx6/sabre-6quad/configs/coredump/defconfig index 8f73d5564a..da7f72396b 100644 --- a/boards/arm/imx6/sabre-6quad/configs/coredump/defconfig +++ b/boards/arm/imx6/sabre-6quad/configs/coredump/defconfig @@ -17,7 +17,7 @@ CONFIG_ARCH_INTERRUPTSTACK=2048 CONFIG_ARCH_IRQBUTTONS=y CONFIG_ARCH_LOWVECTORS=y CONFIG_ARCH_STACKDUMP=y -CONFIG_BOARD_COREDUMP=y +CONFIG_BOARD_COREDUMP_SYSLOG=y CONFIG_BOARD_LOOPSPERMSEC=99369 CONFIG_BOOT_RUNFROMSDRAM=y CONFIG_BUILTIN=y diff --git a/include/nuttx/binfmt/binfmt.h b/include/nuttx/binfmt/binfmt.h index cc4c31ecaa..9fe9c47ed5 100644 --- a/include/nuttx/binfmt/binfmt.h +++ b/include/nuttx/binfmt/binfmt.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -38,7 +39,8 @@ * Pre-processor Definitions ****************************************************************************/ -#define BINFMT_NALLOC 4 +#define BINFMT_NALLOC 4 +#define COREDUMP_MAGIC 0x434f5245 /**************************************************************************** * Public Types @@ -138,6 +140,16 @@ struct binfmt_s pid_t pid); }; +/* Coredump information for block header */ + +struct coredump_info_s +{ + uint32_t magic; + struct utsname name; + time_t time; + size_t size; +}; + /**************************************************************************** * Public Data ****************************************************************************/ diff --git a/include/nuttx/memoryregion.h b/include/nuttx/memoryregion.h index 47ebafd712..6499e10bcd 100644 --- a/include/nuttx/memoryregion.h +++ b/include/nuttx/memoryregion.h @@ -63,4 +63,34 @@ ssize_t parse_memory_region(FAR const char *format, FAR struct memory_region_s *region, size_t num); +/**************************************************************************** + * Name: alloc_memory_region + * + * Input Parameters: + * format - The format string to parse. ,,,... + * start - The start address of the memory region + * end - The end address of the memory region + * flags - Readable 0x1, writable 0x2, executable 0x4 + * example: 0x1000,0x2000,0x1,0x2000,0x3000,0x3,0x3000,0x4000,0x7 + * + * Return: + * The parsed memory region list on success; NULL on failure. + * The boundary value of the memory region is zero. + * The return value need free by caller. + * + ****************************************************************************/ + +FAR struct memory_region_s * +alloc_memory_region(FAR const char *format); + +/**************************************************************************** + * Name: free_memory_region + * + * Input Parameters: + * region - The memory region list to free. + * + ****************************************************************************/ + +void free_memory_region(FAR struct memory_region_s *region); + #endif /* __INCLUDE_MEMORYREGION_H */ diff --git a/libs/libc/misc/lib_memoryregion.c b/libs/libc/misc/lib_memoryregion.c index addb9947e3..b94a5f8af1 100644 --- a/libs/libc/misc/lib_memoryregion.c +++ b/libs/libc/misc/lib_memoryregion.c @@ -23,6 +23,7 @@ ****************************************************************************/ #include +#include #include #include @@ -52,11 +53,28 @@ ssize_t parse_memory_region(FAR const char *format, FAR char *endptr; size_t i = 0; - if (format == NULL || region == NULL || num == 0) + if (format == NULL) { return -EINVAL; } + if (num == 0 || region == NULL) + { + num = 0; + while (format[i] != '\0') + { + if (format[i++] == ',') + { + num++; + } + } + } + + if (region == NULL) + { + return num / 3 + 1; + } + while (*format != '\0' && i < num * 3) { if (i % 3 == 0) @@ -78,3 +96,57 @@ ssize_t parse_memory_region(FAR const char *format, return i / 3; } + +/**************************************************************************** + * Name: alloc_memory_region + * + * Input Parameters: + * format - The format string to parse. ,,,... + * start - The start address of the memory region + * end - The end address of the memory region + * flags - Readable 0x1, writable 0x2, executable 0x4 + * example: 0x1000,0x2000,0x1,0x2000,0x3000,0x3,0x3000,0x4000,0x7 + * + * Return: + * The parsed memory region list on success; NULL on failure. + * The boundary value of the memory region is zero. + * The return value need free by caller. + * + ****************************************************************************/ + +FAR struct memory_region_s * +alloc_memory_region(FAR const char *format) +{ + FAR struct memory_region_s *region; + ssize_t num = parse_memory_region(format, NULL, 0); + + if (num < 0) + { + return NULL; + } + + region = lib_zalloc(sizeof(struct memory_region_s) * (num + 1)); + if (region == NULL) + { + return NULL; + } + + parse_memory_region(format, region, num); + return region; +} + +/**************************************************************************** + * Name: free_memory_region + * + * Input Parameters: + * region - The memory region list to free. + * + ****************************************************************************/ + +void free_memory_region(FAR struct memory_region_s *region) +{ + if (region != NULL) + { + lib_free(region); + } +} diff --git a/sched/init/nx_bringup.c b/sched/init/nx_bringup.c index 7f32c60c3a..6244908ad3 100644 --- a/sched/init/nx_bringup.c +++ b/sched/init/nx_bringup.c @@ -48,6 +48,7 @@ #include "sched/sched.h" #include "wqueue/wqueue.h" #include "init/init.h" +#include "misc/coredump.h" /**************************************************************************** * Pre-processor Definitions @@ -248,6 +249,11 @@ static inline void nx_start_application(void) board_late_initialize(); #endif +#if defined(CONFIG_BOARD_COREDUMP_SYSLOG) || \ + defined(CONFIG_BOARD_COREDUMP_BLKDEV) + coredump_initialize(); +#endif + posix_spawnattr_init(&attr); attr.priority = CONFIG_INIT_PRIORITY; attr.stacksize = CONFIG_INIT_STACKSIZE; diff --git a/sched/misc/CMakeLists.txt b/sched/misc/CMakeLists.txt index a92492870c..dc410e7121 100644 --- a/sched/misc/CMakeLists.txt +++ b/sched/misc/CMakeLists.txt @@ -28,4 +28,8 @@ if(CONFIG_DUMP_ON_EXIT) list(APPEND SRCS dump.c) endif() +if(CONFIG_BOARD_COREDUMP_SYSLOG OR CONFIG_BOARD_COREDUMP_BLKDEV) + list(APPEND SRCS coredump.c) +endif() + target_sources(sched PRIVATE ${SRCS}) diff --git a/sched/misc/Make.defs b/sched/misc/Make.defs index 35291122f7..08142fadf3 100644 --- a/sched/misc/Make.defs +++ b/sched/misc/Make.defs @@ -28,6 +28,10 @@ ifeq ($(CONFIG_DUMP_ON_EXIT),y) CSRCS += dump.c endif +ifneq ($(CONFIG_BOARD_COREDUMP_SYSLOG)$(CONFIG_BOARD_COREDUMP_BLKDEV),) +CSRCS += coredump.c +endif + # Include init build support DEPPATH += --dep-path misc diff --git a/sched/misc/assert.c b/sched/misc/assert.c index b4e5c48535..b63f664c28 100644 --- a/sched/misc/assert.c +++ b/sched/misc/assert.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -47,6 +46,7 @@ #include "irq/irq.h" #include "sched/sched.h" #include "group/group.h" +#include "misc/coredump.h" /**************************************************************************** * Pre-processor Definitions @@ -77,15 +77,6 @@ ****************************************************************************/ static uintptr_t g_last_regs[XCPTCONTEXT_REGS] aligned_data(16); - -#ifdef CONFIG_BOARD_COREDUMP -static struct lib_syslogstream_s g_syslogstream; -static struct lib_hexdumpstream_s g_hexstream; -# ifdef CONFIG_BOARD_COREDUMP_COMPRESSION -static struct lib_lzfoutstream_s g_lzfstream; -# endif -#endif - static FAR const char *g_policy[4] = { "FIFO", "RR", "SPORADIC" @@ -481,53 +472,6 @@ static void dump_tasks(void) #endif } -/**************************************************************************** - * Name: dump_core - ****************************************************************************/ - -#ifdef CONFIG_BOARD_COREDUMP -static void dump_core(pid_t pid) -{ - FAR void *stream; - int logmask; - - logmask = setlogmask(LOG_ALERT); - - _alert("Start coredump:\n"); - - /* Initialize hex output stream */ - - lib_syslogstream(&g_syslogstream, LOG_EMERG); - - stream = &g_syslogstream; - - lib_hexdumpstream(&g_hexstream, stream); - - stream = &g_hexstream; - -# ifdef CONFIG_BOARD_COREDUMP_COMPRESSION - - /* Initialize LZF compression stream */ - - lib_lzfoutstream(&g_lzfstream, stream); - stream = &g_lzfstream; - -# endif - - /* Do core dump */ - - core_dump(NULL, stream, pid); - -# ifdef CONFIG_BOARD_COREDUMP_COMPRESSION - _alert("Finish coredump (Compression Enabled).\n"); -# else - _alert("Finish coredump.\n"); -# endif - - setlogmask(logmask); -} -#endif - /**************************************************************************** * Name: dump_deadlock ****************************************************************************/ @@ -681,16 +625,17 @@ void _assert(FAR const char *filename, int linenum, #ifdef CONFIG_BOARD_CRASHDUMP board_crashdump(up_getsp(), rtcb, filename, linenum, msg, regs); +#endif -#elif defined(CONFIG_BOARD_COREDUMP) +#if defined(CONFIG_BOARD_COREDUMP_SYSLOG) || \ + defined(CONFIG_BOARD_COREDUMP_BLKDEV) /* Dump core information */ # ifdef CONFIG_BOARD_COREDUMP_FULL - dump_core(INVALID_PROCESS_ID); + coredump_dump(INVALID_PROCESS_ID); # else - dump_core(rtcb->pid); + coredump_dump(rtcb->pid); # endif - #endif /* Flush any buffered SYSLOG data */ diff --git a/sched/misc/coredump.c b/sched/misc/coredump.c new file mode 100644 index 0000000000..fcf1a8ca16 --- /dev/null +++ b/sched/misc/coredump.c @@ -0,0 +1,228 @@ +/**************************************************************************** + * sched/misc/coredump.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "misc/coredump.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_BOARD_COREDUMP_COMPRESSION +static struct lib_lzfoutstream_s g_lzfstream; +#endif + +#ifdef CONFIG_BOARD_COREDUMP_SYSLOG +static struct lib_syslogstream_s g_syslogstream; +static struct lib_hexdumpstream_s g_hexstream; +#endif + +#ifdef CONFIG_BOARD_COREDUMP_BLKDEV +static struct lib_blkoutstream_s g_blockstream; +static unsigned char *g_blockinfo; +#endif + +static struct memory_region_s *g_regions; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: coredump_dump_syslog + * + * Description: + * Put coredump to block device. + * + ****************************************************************************/ + +#ifdef CONFIG_BOARD_COREDUMP_SYSLOG +static void coredump_dump_syslog(pid_t pid) +{ + FAR void *stream; + int logmask; + + logmask = setlogmask(LOG_ALERT); + + _alert("Start coredump:\n"); + + /* Initialize hex output stream */ + + lib_syslogstream(&g_syslogstream, LOG_EMERG); + stream = &g_syslogstream; + lib_hexdumpstream(&g_hexstream, stream); + stream = &g_hexstream; +# ifdef CONFIG_BOARD_COREDUMP_COMPRESSION + + /* Initialize LZF compression stream */ + + lib_lzfoutstream(&g_lzfstream, stream); + stream = &g_lzfstream; +# endif + + /* Do core dump */ + + core_dump(g_regions, stream, pid); + +# ifdef CONFIG_BOARD_COREDUMP_COMPRESSION + _alert("Finish coredump (Compression Enabled).\n"); +# else + _alert("Finish coredump.\n"); +# endif + + setlogmask(logmask); +} +#endif + +/**************************************************************************** + * Name: coredump_dump_blkdev + * + * Description: + * Save coredump to block device. + * + ****************************************************************************/ + +#ifdef CONFIG_BOARD_COREDUMP_BLKDEV +static void coredump_dump_blkdev(pid_t pid) +{ + FAR void *stream = &g_blockstream; + FAR struct coredump_info_s *info; + int ret; + + if (g_blockstream.inode == NULL) + { + _alert("Coredump Device Not Found\n"); + return; + } + + ret = g_blockstream.inode->u.i_bops->read(g_blockstream.inode, + g_blockinfo, g_blockstream.geo.geo_nsectors - 1, 1); + if (ret < 0) + { + _alert("Coredump Device Read Fail\n"); + return; + } + + info = (FAR struct coredump_info_s *)g_blockinfo; + if (info->magic == COREDUMP_MAGIC) + { + _alert("Coredump Device Already Used\n"); + return; + } + +#ifdef CONFIG_BOARD_COREDUMP_COMPRESSION + lib_lzfoutstream(&g_lzfstream, + (FAR struct lib_outstream_s *)&g_blockstream); + stream = &g_lzfstream; +#endif + + ret = core_dump(g_regions, stream, pid); + if (ret < 0) + { + _alert("Coredump Fail\n"); + return; + } + + info->magic = COREDUMP_MAGIC; + info->size = g_blockstream.common.nput; + info->time = time(NULL); + uname(&info->name); + g_blockstream.inode->u.i_bops->write(g_blockstream.inode, + (FAR void *)info, g_blockstream.geo.geo_nsectors - 1, 1); +} +#endif + +/**************************************************************************** + * Name: coredump_initialize + * + * Description: + * Initialize the coredump facility. Called once and only from + * nx_start_application. + * + ****************************************************************************/ + +int coredump_initialize(void) +{ + int ret = 0; + + if (CONFIG_BOARD_MEMORY_RANGE[0] != '\0') + { + g_regions = alloc_memory_region(CONFIG_BOARD_MEMORY_RANGE); + if (g_regions == NULL) + { + _alert("Memory Region Alloc Fail\n"); + return -ENOMEM; + } + } + +#ifdef CONFIG_BOARD_COREDUMP_BLKDEV + ret = lib_blkoutstream_open(&g_blockstream, + CONFIG_BOARD_COREDUMP_BLKDEV_PATH); + if (ret < 0) + { + _alert("%s Coredump Device Not Found\n", + CONFIG_BOARD_COREDUMP_BLKDEV_PATH); + free_memory_region(g_regions); + g_regions = NULL; + return ret; + } + + g_blockinfo = kmm_malloc(g_blockstream.geo.geo_sectorsize); + if (g_blockinfo == NULL) + { + _alert("Coredump Device Memory Alloc Fail\n"); + free_memory_region(g_regions); + g_regions = NULL; + lib_blkoutstream_close(&g_blockstream); + return -ENOMEM; + } +#endif + + return ret; +} + +/**************************************************************************** + * Name: coredump_dump + * + * Description: + * Do coredump of the task specified by pid. + * + * Input Parameters: + * pid - The task/thread ID of the thread to dump + * + ****************************************************************************/ + +void coredump_dump(pid_t pid) +{ +#ifdef CONFIG_BOARD_COREDUMP_SYSLOG + coredump_dump_syslog(pid); +#endif + +#ifdef CONFIG_BOARD_COREDUMP_BLKDEV + coredump_dump_blkdev(pid); +#endif +} diff --git a/sched/misc/coredump.h b/sched/misc/coredump.h new file mode 100644 index 0000000000..32dc3a6c2c --- /dev/null +++ b/sched/misc/coredump.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * sched/misc/coredump.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __SCHED_MISC_COREDUMP_H +#define __SCHED_MISC_COREDUMP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: coredump_initialize + * + * Description: + * Initialize the coredump facility. Called once and only from + * nx_start_application. + * + ****************************************************************************/ + +int coredump_initialize(void); + +/**************************************************************************** + * Name: coredump_dump + * + * Description: + * Do coredump of the task specified by pid. + * + * Input Parameters: + * pid - The task/thread ID of the thread to dump + * + ****************************************************************************/ + +void coredump_dump(pid_t pid); + +#endif /* __SCHED_MISC_COREDUMP_H */