assert: cleanup assert handler

1. extract dump from assert main flow
2. use OSINIT_PANIC for fatal error.
3. fix the method to judge kernel thread.

Signed-off-by: xuxingliang <xuxingliang@xiaomi.com>
This commit is contained in:
xuxingliang 2024-07-17 11:11:56 +08:00 committed by Xiang Xiao
parent 9da6761453
commit e65b03edd2
2 changed files with 188 additions and 127 deletions

View File

@ -69,7 +69,8 @@ enum nx_initstate_e
* initialization. */ * initialization. */
OSINIT_OSREADY = 5, /* The OS is fully initialized and multi-tasking is OSINIT_OSREADY = 5, /* The OS is fully initialized and multi-tasking is
* active. */ * active. */
OSINIT_IDLELOOP = 6 /* The OS enter idle loop */ OSINIT_IDLELOOP = 6, /* The OS enter idle loop. */
OSINIT_PANIC = 7 /* Fatal error happened. */
}; };
/**************************************************************************** /****************************************************************************

View File

@ -29,9 +29,9 @@
#include <nuttx/arch.h> #include <nuttx/arch.h>
#include <nuttx/board.h> #include <nuttx/board.h>
#include <nuttx/coredump.h> #include <nuttx/coredump.h>
#include <nuttx/fs/fs.h>
#include <nuttx/init.h> #include <nuttx/init.h>
#include <nuttx/irq.h> #include <nuttx/irq.h>
#include <nuttx/fs/fs.h>
#include <nuttx/tls.h> #include <nuttx/tls.h>
#include <nuttx/signal.h> #include <nuttx/signal.h>
#ifdef CONFIG_ARCH_LEDS #ifdef CONFIG_ARCH_LEDS
@ -94,8 +94,9 @@
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
static uintptr_t static uintptr_t g_last_regs[XCPTCONTEXT_REGS]
g_last_regs[XCPTCONTEXT_REGS] aligned_data(XCPTCONTEXT_ALIGN); aligned_data(XCPTCONTEXT_ALIGN);
static FAR const char * const g_policy[4] = static FAR const char * const g_policy[4] =
{ {
"FIFO", "RR", "SPORADIC" "FIFO", "RR", "SPORADIC"
@ -109,8 +110,6 @@ static FAR const char * const g_ttypenames[4] =
"Invalid" "Invalid"
}; };
static bool g_fatal_assert = false;
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
@ -213,8 +212,14 @@ static void dump_stack(FAR const char *tag, uintptr_t sp,
static void dump_stacks(FAR struct tcb_s *rtcb, uintptr_t sp) static void dump_stacks(FAR struct tcb_s *rtcb, uintptr_t sp)
{ {
#ifdef CONFIG_SMP
int cpu = rtcb->cpu;
#else
int cpu = this_cpu();
UNUSED(cpu);
#endif
#if CONFIG_ARCH_INTERRUPTSTACK > 0 #if CONFIG_ARCH_INTERRUPTSTACK > 0
uintptr_t intstack_base = up_get_intstackbase(this_cpu()); uintptr_t intstack_base = up_get_intstackbase(cpu);
size_t intstack_size = CONFIG_ARCH_INTERRUPTSTACK; size_t intstack_size = CONFIG_ARCH_INTERRUPTSTACK;
uintptr_t intstack_top = intstack_base + intstack_size; uintptr_t intstack_top = intstack_base + intstack_size;
uintptr_t intstack_sp = 0; uintptr_t intstack_sp = 0;
@ -263,13 +268,16 @@ static void dump_stacks(FAR struct tcb_s *rtcb, uintptr_t sp)
intstack_base, intstack_base,
intstack_size, intstack_size,
#ifdef CONFIG_STACK_COLORATION #ifdef CONFIG_STACK_COLORATION
up_check_intstack(this_cpu()) up_check_intstack(cpu)
#else #else
0 0
#endif #endif
); );
tcbstack_sp = up_getusrsp((FAR void *)up_current_regs()); /* Try to restore SP from current_regs if assert from interrupt. */
tcbstack_sp = up_interrupt_context() ?
up_getusrsp((FAR void *)up_current_regs()) : 0;
if (tcbstack_sp < tcbstack_base || tcbstack_sp >= tcbstack_top) if (tcbstack_sp < tcbstack_base || tcbstack_sp >= tcbstack_top)
{ {
tcbstack_sp = 0; tcbstack_sp = 0;
@ -364,7 +372,7 @@ static void dump_task(FAR struct tcb_s *tcb, FAR void *arg)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
" %4d" " %4d"
#endif #endif
" %3d %-8s %-7s %c" " %3d %-8s %-7s %-3c"
" %-18s" " %-18s"
" " SIGSET_FMT " " SIGSET_FMT
" %p" " %p"
@ -562,95 +570,47 @@ static void pause_all_cpu(void)
} }
#endif #endif
/**************************************************************************** static void dump_running_task(FAR struct tcb_s *rtcb, FAR void *regs)
* Public Functions {
****************************************************************************/ /* Register dump */
/**************************************************************************** up_dump_register(regs);
* Name: _assert
****************************************************************************/ #ifdef CONFIG_ARCH_STACKDUMP
dump_stacks(rtcb, up_getusrsp(regs));
void _assert(FAR const char *filename, int linenum, #endif
FAR const char *msg, FAR void *regs)
/* Show back trace */
#ifdef CONFIG_SCHED_BACKTRACE
sched_dumpstack(rtcb->pid);
#endif
}
/****************************************************************************
* Name: dump_assert_info
*
* Description:
* Dump basic information of assertion
****************************************************************************/
static void dump_assert_info(FAR struct tcb_s *rtcb,
FAR const char *filename, int linenum,
FAR const char *msg, FAR void *regs)
{ {
const bool os_ready = OSINIT_OS_READY();
FAR struct tcb_s *rtcb = running_task();
#if CONFIG_TASK_NAME_SIZE > 0 #if CONFIG_TASK_NAME_SIZE > 0
FAR struct tcb_s *ptcb = NULL; FAR struct tcb_s *ptcb = NULL;
#endif #endif
struct panic_notifier_s notifier_data;
struct utsname name; struct utsname name;
irqstate_t flags;
bool fatal = true;
#if CONFIG_TASK_NAME_SIZE > 0 #if CONFIG_TASK_NAME_SIZE > 0
if (rtcb->group && !(rtcb->flags & TCB_FLAG_TTYPE_KERNEL)) if (rtcb->group &&
(rtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL)
{ {
ptcb = nxsched_get_tcb(rtcb->group->tg_pid); ptcb = nxsched_get_tcb(rtcb->group->tg_pid);
} }
#endif #endif
flags = 0; /* suppress GCC warning */
if (os_ready)
{
flags = enter_critical_section();
}
if (g_fatal_assert)
{
goto reset;
}
if (os_ready && fatal)
{
/* Disable KASAN to avoid false positive */
kasan_stop();
#ifdef CONFIG_SMP
pause_all_cpu();
#endif
}
/* try to save current context if regs is null */
if (regs == NULL)
{
up_saveusercontext(g_last_regs);
regs = g_last_regs;
}
else
{
memcpy(g_last_regs, regs, sizeof(g_last_regs));
}
#if CONFIG_BOARD_RESET_ON_ASSERT < 2
if (!up_interrupt_context() &&
(rtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_KERNEL)
{
fatal = false;
}
else
#endif
{
g_fatal_assert = true;
}
notifier_data.rtcb = rtcb;
notifier_data.regs = regs;
notifier_data.filename = filename;
notifier_data.linenum = linenum;
notifier_data.msg = msg;
panic_notifier_call_chain(fatal ? PANIC_KERNEL : PANIC_TASK,
&notifier_data);
#ifdef CONFIG_ARCH_LEDS
board_autoled_on(LED_ASSERTION);
#endif
/* Flush any buffered SYSLOG data (from prior to the assertion) */
syslog_flush();
uname(&name); uname(&name);
_alert("Current Version: %s %s %s %s %s\n", _alert("Current Version: %s %s %s %s %s\n",
name.sysname, name.nodename, name.sysname, name.nodename,
@ -677,42 +637,41 @@ void _assert(FAR const char *filename, int linenum,
#endif #endif
rtcb->entry.main); rtcb->entry.main);
/* Register dump */ /* Dump current CPU registers, running task stack and backtrace. */
up_dump_register(regs); dump_running_task(rtcb, regs);
#ifdef CONFIG_ARCH_STACKDUMP
dump_stacks(rtcb, up_getusrsp(regs));
#endif
/* Show back trace */
#ifdef CONFIG_SCHED_BACKTRACE
sched_dumpstack(rtcb->pid);
#endif
/* Flush any buffered SYSLOG data */ /* Flush any buffered SYSLOG data */
syslog_flush(); syslog_flush();
}
if (fatal) /****************************************************************************
{ * Name: dump_fatal_info
dump_tasks(); ****************************************************************************/
static void dump_fatal_info(FAR struct tcb_s *rtcb,
FAR const char *filename, int linenum,
FAR const char *msg, FAR void *regs)
{
/* Dump backtrace of other tasks. */
dump_tasks();
#ifdef CONFIG_ARCH_DEADLOCKDUMP #ifdef CONFIG_ARCH_DEADLOCKDUMP
/* Deadlock Dump */ /* Deadlock Dump */
dump_deadlock(); dump_deadlock();
#endif #endif
#ifdef CONFIG_ARCH_USBDUMP #ifdef CONFIG_ARCH_USBDUMP
/* Dump USB trace data */ /* Dump USB trace data */
usbtrace_enumerate(assert_tracecallback, NULL); usbtrace_enumerate(assert_tracecallback, NULL);
#endif #endif
#ifdef CONFIG_BOARD_CRASHDUMP #ifdef CONFIG_BOARD_CRASHDUMP
board_crashdump(up_getsp(), rtcb, filename, linenum, msg, regs); board_crashdump(up_getsp(), rtcb, filename, linenum, msg, regs);
#endif #endif
#if defined(CONFIG_BOARD_COREDUMP_SYSLOG) || \ #if defined(CONFIG_BOARD_COREDUMP_SYSLOG) || \
@ -720,35 +679,136 @@ void _assert(FAR const char *filename, int linenum,
/* Dump core information */ /* Dump core information */
# ifdef CONFIG_BOARD_COREDUMP_FULL # ifdef CONFIG_BOARD_COREDUMP_FULL
coredump_dump(INVALID_PROCESS_ID); coredump_dump(INVALID_PROCESS_ID);
# else # else
coredump_dump(rtcb->pid); coredump_dump(rtcb->pid);
# endif # endif
#endif #endif
/* Flush any buffered SYSLOG data */ /* Flush any buffered SYSLOG data */
syslog_flush();
}
/****************************************************************************
* Name: reset_board
*
* Description:
* Reset board or stuck here to flash LED. It should never return.
****************************************************************************/
static void reset_board(void)
{
#if CONFIG_BOARD_RESET_ON_ASSERT >= 1
board_reset(CONFIG_BOARD_ASSERT_RESET_VALUE);
#else
for (; ; )
{
#ifdef CONFIG_ARCH_LEDS
/* FLASH LEDs a 2Hz */
board_autoled_on(LED_PANIC);
up_mdelay(250);
board_autoled_off(LED_PANIC);
#endif
up_mdelay(250);
}
#endif
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: _assert
****************************************************************************/
void _assert(FAR const char *filename, int linenum,
FAR const char *msg, FAR void *regs)
{
const bool os_ready = OSINIT_OS_READY();
FAR struct tcb_s *rtcb = running_task();
struct panic_notifier_s notifier_data;
irqstate_t flags;
if (g_nx_initstate == OSINIT_PANIC)
{
/* Already in fatal state, reset board directly. */
reset_board(); /* Should not return. */
}
flags = 0; /* suppress GCC warning */
if (os_ready)
{
flags = enter_critical_section();
}
#if CONFIG_BOARD_RESET_ON_ASSERT < 2
if (up_interrupt_context() ||
(rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
#endif
{
/* Fatal error, enter panic state. */
g_nx_initstate = OSINIT_PANIC;
/* Disable KASAN to avoid false positive */
kasan_stop();
#ifdef CONFIG_SMP
if (os_ready)
{
pause_all_cpu();
}
#endif
}
/* try to save current context if regs is null */
if (regs == NULL)
{
up_saveusercontext(g_last_regs);
regs = g_last_regs;
}
else
{
memcpy(g_last_regs, regs, sizeof(g_last_regs));
}
notifier_data.rtcb = rtcb;
notifier_data.regs = regs;
notifier_data.filename = filename;
notifier_data.linenum = linenum;
notifier_data.msg = msg;
panic_notifier_call_chain(g_nx_initstate == OSINIT_PANIC
? PANIC_KERNEL : PANIC_TASK,
&notifier_data);
#ifdef CONFIG_ARCH_LEDS
board_autoled_on(LED_ASSERTION);
#endif
/* Flush any buffered SYSLOG data (from prior to the assertion) */
syslog_flush();
/* Dump basic info of assertion. */
dump_assert_info(rtcb, filename, linenum, msg, regs);
if (g_nx_initstate == OSINIT_PANIC)
{
/* Dump fatal info of assertion. */
dump_fatal_info(rtcb, filename, linenum, msg, regs);
syslog_flush();
panic_notifier_call_chain(PANIC_KERNEL_FINAL, &notifier_data); panic_notifier_call_chain(PANIC_KERNEL_FINAL, &notifier_data);
reboot_notifier_call_chain(SYS_HALT, NULL); reboot_notifier_call_chain(SYS_HALT, NULL);
reset: reset_board(); /* Should not return. */
#if CONFIG_BOARD_RESET_ON_ASSERT >= 1
board_reset(CONFIG_BOARD_ASSERT_RESET_VALUE);
#else
for (; ; )
{
#ifdef CONFIG_ARCH_LEDS
/* FLASH LEDs a 2Hz */
board_autoled_on(LED_PANIC);
up_mdelay(250);
board_autoled_off(LED_PANIC);
#endif
up_mdelay(250);
}
#endif
} }
if (os_ready) if (os_ready)