Skip to content

Commit 588d9ed

Browse files
committed
feat(ulog): add emergency log flush mechanism
1 parent ba509f9 commit 588d9ed

File tree

5 files changed

+157
-11
lines changed

5 files changed

+157
-11
lines changed

components/utilities/ulog/backend/console_be.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Change Logs:
77
* Date Author Notes
88
* 2018-09-04 armink the first version
9+
* 2025-10-30 wdfk-prog add emergency log flush mechanism
910
*/
1011

1112
#include <rthw.h>
@@ -32,6 +33,8 @@ int ulog_console_backend_init(void)
3233
console.output = ulog_console_backend_output;
3334

3435
ulog_backend_register(&console, "console", RT_TRUE);
36+
console.output = ulog_console_backend_output;
37+
console.is_emergency_backend = RT_TRUE;
3538

3639
return 0;
3740
}

components/utilities/ulog/ulog.c

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Change Logs:
77
* Date Author Notes
88
* 2018-08-25 armink the first version
9+
* 2025-10-30 wdfk-prog add emergency log flush mechanism
910
*/
1011

1112
#include <stdarg.h>
@@ -501,7 +502,23 @@ rt_weak rt_size_t ulog_hex_formater(char *log_buf, const char *tag, const rt_uin
501502
return ulog_tail_formater(log_buf, log_len, RT_TRUE, LOG_LVL_DBG);
502503
}
503504

504-
static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
505+
/**
506+
* @brief Internal helper to broadcast a log to backends.
507+
*
508+
* @details This is the core broadcasting function. It iterates through all
509+
* registered backends and outputs the log message. It can operate
510+
* in two modes.
511+
*
512+
* @param emergency_only If RT_TRUE, it will only output to backends marked as
513+
* `is_emergency_backend`. If RT_FALSE, it will output to
514+
* all backends that match the filter criteria.
515+
* @param level The log level.
516+
* @param tag The log tag.
517+
* @param is_raw Whether the log is raw data.
518+
* @param log The log message buffer.
519+
* @param len The length of the log message.
520+
*/
521+
static void _ulog_output_to_backends(rt_bool_t emergency_only, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
505522
{
506523
rt_slist_t *node;
507524
ulog_backend_t backend;
@@ -520,6 +537,13 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
520537
for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
521538
{
522539
backend = rt_slist_entry(node, struct ulog_backend, list);
540+
541+
if (emergency_only && !backend->is_emergency_backend)
542+
{
543+
/* In emergency mode, skip any backend not marked as emergency-safe. */
544+
continue;
545+
}
546+
523547
if (backend->out_level < level)
524548
{
525549
continue;
@@ -558,6 +582,11 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
558582
}
559583
}
560584

585+
static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len)
586+
{
587+
_ulog_output_to_backends(RT_FALSE, level, tag, is_raw, log, len);
588+
}
589+
561590
static void do_output(rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log_buf, rt_size_t log_len)
562591
{
563592
#ifdef ULOG_USING_ASYNC_OUTPUT
@@ -1297,6 +1326,7 @@ rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool
12971326
backend->support_color = support_color;
12981327
backend->out_level = LOG_FILTER_LVL_ALL;
12991328
rt_strncpy(backend->name, name, RT_NAME_MAX - 1);
1329+
backend->is_emergency_backend = RT_FALSE;
13001330

13011331
level = rt_spin_lock_irqsave(&_spinlock);
13021332
rt_slist_append(&ulog.backend_list, &backend->list);
@@ -1366,13 +1396,54 @@ ulog_backend_t ulog_backend_find(const char *name)
13661396
return RT_NULL;
13671397
}
13681398

1399+
/**
1400+
* @brief Sets the emergency-safe status for a specific backend at runtime.
1401+
*
1402+
* @details This function allows an application to dynamically mark a backend
1403+
* as safe or unsafe for use in emergency contexts (e.g., fault handlers).
1404+
* Only backends marked as safe will be used by `ulog_emergency_flush`.
1405+
*
1406+
* @param[in] name The name of the target backend.
1407+
* @param[in] is_emergency RT_TRUE to mark the backend as safe for emergency calls,
1408+
* RT_FALSE otherwise.
1409+
*
1410+
* @return rt_err_t RT_EOK on success, -RT_ERROR if the backend with the given name
1411+
* is not found, or -RT_EINVAL if the name is invalid.
1412+
*/
1413+
rt_err_t ulog_backend_set_emergency(const char *name, rt_bool_t is_emergency)
1414+
{
1415+
ulog_backend_t backend;
1416+
rt_err_t result = -RT_ERROR;
1417+
1418+
if (name == RT_NULL)
1419+
{
1420+
return -RT_EINVAL;
1421+
}
1422+
1423+
rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
1424+
1425+
backend = ulog_backend_find(name);
1426+
if (backend != RT_NULL)
1427+
{
1428+
backend->is_emergency_backend = is_emergency;
1429+
result = RT_EOK;
1430+
}
1431+
1432+
rt_mutex_release(&ulog.output_locker);
1433+
1434+
return result;
1435+
}
1436+
13691437
#ifdef ULOG_USING_ASYNC_OUTPUT
13701438
/**
13711439
* asynchronous output logs to all backends
13721440
*
1441+
* @param[in] emergency_mode A flag to select the operational mode. RT_FALSE for
1442+
* normal operation, RT_TRUE for emergency fault context.
1443+
*
13731444
* @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
13741445
*/
1375-
void ulog_async_output(void)
1446+
void ulog_async_output(rt_bool_t emergency_mode)
13761447
{
13771448
rt_rbb_blk_t log_blk;
13781449
ulog_frame_t log_frame;
@@ -1387,14 +1458,12 @@ void ulog_async_output(void)
13871458
log_frame = (ulog_frame_t) log_blk->buf;
13881459
if (log_frame->magic == ULOG_FRAME_MAGIC)
13891460
{
1390-
/* output to all backends */
1391-
ulog_output_to_all_backend(log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log,
1392-
log_frame->log_len);
1461+
_ulog_output_to_backends(emergency_mode, log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log, log_frame->log_len);
13931462
}
13941463
rt_rbb_blk_free(ulog.async_rbb, log_blk);
13951464
}
13961465
/* output the log_raw format log */
1397-
if (ulog.async_rb)
1466+
if (ulog.async_rb && !emergency_mode)
13981467
{
13991468
rt_size_t log_len = rt_ringbuffer_data_len(ulog.async_rb);
14001469
char *log = rt_malloc(log_len + 1);
@@ -1434,14 +1503,14 @@ rt_err_t ulog_async_waiting_log(rt_int32_t time)
14341503

14351504
static void async_output_thread_entry(void *param)
14361505
{
1437-
ulog_async_output();
1506+
ulog_async_output(RT_FALSE);
14381507

14391508
while (1)
14401509
{
14411510
ulog_async_waiting_log(RT_WAITING_FOREVER);
14421511
while (1)
14431512
{
1444-
ulog_async_output();
1513+
ulog_async_output(RT_FALSE);
14451514
/* If there is no log output for a certain period of time,
14461515
* refresh the log buffer
14471516
*/
@@ -1471,7 +1540,7 @@ void ulog_flush(void)
14711540
return;
14721541

14731542
#ifdef ULOG_USING_ASYNC_OUTPUT
1474-
ulog_async_output();
1543+
ulog_async_output(RT_FALSE);
14751544
#endif
14761545

14771546
/* flush all backends */
@@ -1485,6 +1554,36 @@ void ulog_flush(void)
14851554
}
14861555
}
14871556

1557+
/**
1558+
* @brief Flushes pending logs and outputs to emergency-safe backends.
1559+
*/
1560+
void ulog_emergency_flush(void)
1561+
{
1562+
rt_slist_t *node;
1563+
ulog_backend_t backend;
1564+
rt_base_t irq_flag;
1565+
1566+
if (!ulog.init_ok)
1567+
return;
1568+
1569+
irq_flag = rt_hw_interrupt_disable();
1570+
1571+
#ifdef ULOG_USING_ASYNC_OUTPUT
1572+
ulog_async_output(RT_TRUE);
1573+
#endif
1574+
1575+
rt_slist_for_each(node, &ulog.backend_list)
1576+
{
1577+
backend = rt_slist_entry(node, struct ulog_backend, list);
1578+
if (backend->is_emergency_backend && backend->flush)
1579+
{
1580+
backend->flush(backend);
1581+
}
1582+
}
1583+
1584+
rt_hw_interrupt_enable(irq_flag);
1585+
}
1586+
14881587
/**
14891588
* @brief ulog initialization
14901589
*

components/utilities/ulog/ulog.h

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Change Logs:
77
* Date Author Notes
88
* 2018-08-25 armink the first version
9+
* 2025-10-30 wdfk-prog add emergency log flush mechanism
910
*/
1011

1112
#ifndef _ULOG_H_
@@ -47,13 +48,52 @@ void ulog_deinit(void);
4748
#define LOG_RAW(...) ulog_raw(__VA_ARGS__)
4849
#define LOG_HEX(name, width, buf, size) ulog_hex(name, width, buf, size)
4950

51+
/**
52+
* @brief Output an emergency log and attempt an immediate, safe flush.
53+
*
54+
* @details This macro is specifically designed for use in critical system states
55+
* where standard logging mechanisms might be unreliable, such as in
56+
* assertion handlers, HardFault handlers, or stack overflow hooks.
57+
*
58+
* It operates in two steps:
59+
* 1. It calls ulog_e() to place the log message into the ulog buffer.
60+
* 2. It immediately calls ulog_emergency_flush(). This special flush
61+
* function is designed to bypass potentially unsafe OS services (like
62+
* mutexes or complex file systems). It will only flush the log to
63+
* backends that have been explicitly marked as "emergency safe"
64+
* (where is_emergency_backend is set to RT_TRUE), such as a simple
65+
* console or polled UART backend.
66+
*
67+
* This provides the highest possible chance of outputting critical debug
68+
* information just before a system crash or lock-up.
69+
*
70+
* @note The LOG_TAG macro must be defined before using this API.
71+
*
72+
* @example
73+
* // In a stack overflow hook
74+
* #define LOG_TAG "StackCheck"
75+
* #include <ulog.h>
76+
* void rt_err_hook(rt_err_t err, const char* msg, rt_uint32_t* sp)
77+
* {
78+
* LOG_EMERGENCY("Stack overflow detected! Msg: %s, SP: 0x%08X", msg, sp);
79+
* // The system will likely halt after this.
80+
* }
81+
*/
82+
#define LOG_EMERGENCY(...) \
83+
do \
84+
{ \
85+
ulog_e(LOG_TAG, __VA_ARGS__); \
86+
ulog_emergency_flush(); \
87+
} while (0)
88+
5089
/*
5190
* backend register and unregister
5291
*/
5392
rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool_t support_color);
5493
rt_err_t ulog_backend_unregister(ulog_backend_t backend);
5594
rt_err_t ulog_backend_set_filter(ulog_backend_t backend, ulog_backend_filter_t filter);
5695
ulog_backend_t ulog_backend_find(const char *name);
96+
rt_err_t ulog_backend_set_emergency(const char *name, rt_bool_t is_emergency);
5797

5898
#ifdef ULOG_USING_FILTER
5999
/*
@@ -74,12 +114,13 @@ const char *ulog_global_filter_kw_get(void);
74114
* flush all backends's log
75115
*/
76116
void ulog_flush(void);
117+
void ulog_emergency_flush(void);
77118

78119
#ifdef ULOG_USING_ASYNC_OUTPUT
79120
/*
80121
* asynchronous output API
81122
*/
82-
void ulog_async_output(void);
123+
void ulog_async_output(rt_bool_t emergency_mode);
83124
void ulog_async_output_enabled(rt_bool_t enabled);
84125
rt_err_t ulog_async_waiting_log(rt_int32_t time);
85126
#endif

components/utilities/ulog/ulog_def.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Change Logs:
77
* Date Author Notes
88
* 2018-08-25 armink the first version
9+
* 2025-10-30 wdfk-prog add emergency log flush mechanism
910
*/
1011

1112
#ifndef _ULOG_DEF_H_
@@ -198,6 +199,7 @@ struct ulog_backend
198199
char name[RT_NAME_MAX];
199200
rt_bool_t support_color;
200201
rt_uint32_t out_level;
202+
rt_bool_t is_emergency_backend; /**< Can this backend be called safely in a fault/emergency context? */
201203
void (*init) (struct ulog_backend *backend);
202204
void (*output)(struct ulog_backend *backend, rt_uint32_t level, const char *tag, rt_bool_t is_raw, const char *log, rt_size_t len);
203205
void (*flush) (struct ulog_backend *backend);

src/scheduler_comm.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* Date Author Notes
1010
* 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.*
1111
* 2025-09-01 Rbb666 Add thread stack overflow hook.
12+
* 2025-10-30 wdfk-prog add emergency log flush mechanism
1213
*/
1314

1415
#define DBG_TAG "kernel.sched"
@@ -484,7 +485,7 @@ void rt_scheduler_stack_check(struct rt_thread *thread)
484485
rt_base_t dummy = 1;
485486
rt_err_t hook_result = -RT_ERROR;
486487

487-
LOG_E("thread:%s stack overflow\n", thread->parent.name);
488+
LOG_EMERGENCY("thread:%s stack overflow\n", thread->parent.name);
488489

489490
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
490491
if (rt_stack_overflow_hook != RT_NULL)

0 commit comments

Comments
 (0)