Skip to content

Commit 275c783

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

File tree

5 files changed

+114
-10
lines changed

5 files changed

+114
-10
lines changed

components/utilities/ulog/backend/console_be.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ int ulog_console_backend_init(void)
3232
console.output = ulog_console_backend_output;
3333

3434
ulog_backend_register(&console, "console", RT_TRUE);
35+
console.output = ulog_console_backend_output;
36+
console.is_emergency_backend = RT_TRUE;
3537

3638
return 0;
3739
}

components/utilities/ulog/ulog.c

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,23 @@ rt_weak rt_size_t ulog_hex_formater(char *log_buf, const char *tag, const rt_uin
501501
return ulog_tail_formater(log_buf, log_len, RT_TRUE, LOG_LVL_DBG);
502502
}
503503

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)
504+
/**
505+
* @brief Internal helper to broadcast a log to backends.
506+
*
507+
* @details This is the core broadcasting function. It iterates through all
508+
* registered backends and outputs the log message. It can operate
509+
* in two modes.
510+
*
511+
* @param emergency_only If RT_TRUE, it will only output to backends marked as
512+
* `is_emergency_backend`. If RT_FALSE, it will output to
513+
* all backends that match the filter criteria.
514+
* @param level The log level.
515+
* @param tag The log tag.
516+
* @param is_raw Whether the log is raw data.
517+
* @param log The log message buffer.
518+
* @param len The length of the log message.
519+
*/
520+
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)
505521
{
506522
rt_slist_t *node;
507523
ulog_backend_t backend;
@@ -520,6 +536,13 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
520536
for (node = rt_slist_first(&ulog.backend_list); node; node = rt_slist_next(node))
521537
{
522538
backend = rt_slist_entry(node, struct ulog_backend, list);
539+
540+
if (emergency_only && !backend->is_emergency_backend)
541+
{
542+
/* In emergency mode, skip any backend not marked as emergency-safe. */
543+
continue;
544+
}
545+
523546
if (backend->out_level < level)
524547
{
525548
continue;
@@ -558,6 +581,11 @@ static void ulog_output_to_all_backend(rt_uint32_t level, const char *tag, rt_bo
558581
}
559582
}
560583

584+
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)
585+
{
586+
_ulog_output_to_backends(RT_FALSE, level, tag, is_raw, log, len);
587+
}
588+
561589
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)
562590
{
563591
#ifdef ULOG_USING_ASYNC_OUTPUT
@@ -1297,6 +1325,7 @@ rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool
12971325
backend->support_color = support_color;
12981326
backend->out_level = LOG_FILTER_LVL_ALL;
12991327
rt_strncpy(backend->name, name, RT_NAME_MAX - 1);
1328+
backend->is_emergency_backend = RT_FALSE;
13001329

13011330
level = rt_spin_lock_irqsave(&_spinlock);
13021331
rt_slist_append(&ulog.backend_list, &backend->list);
@@ -1366,13 +1395,54 @@ ulog_backend_t ulog_backend_find(const char *name)
13661395
return RT_NULL;
13671396
}
13681397

1398+
/**
1399+
* @brief Sets the emergency-safe status for a specific backend at runtime.
1400+
*
1401+
* @details This function allows an application to dynamically mark a backend
1402+
* as safe or unsafe for use in emergency contexts (e.g., fault handlers).
1403+
* Only backends marked as safe will be used by `ulog_emergency_flush`.
1404+
*
1405+
* @param[in] name The name of the target backend.
1406+
* @param[in] is_emergency RT_TRUE to mark the backend as safe for emergency calls,
1407+
* RT_FALSE otherwise.
1408+
*
1409+
* @return rt_err_t RT_EOK on success, -RT_ERROR if the backend with the given name
1410+
* is not found, or -RT_EINVAL if the name is invalid.
1411+
*/
1412+
rt_err_t ulog_backend_set_emergency(const char *name, rt_bool_t is_emergency)
1413+
{
1414+
ulog_backend_t backend;
1415+
rt_err_t result = -RT_ERROR;
1416+
1417+
if (name == RT_NULL)
1418+
{
1419+
return -RT_EINVAL;
1420+
}
1421+
1422+
rt_mutex_take(&ulog.output_locker, RT_WAITING_FOREVER);
1423+
1424+
backend = ulog_backend_find(name);
1425+
if (backend != RT_NULL)
1426+
{
1427+
backend->is_emergency_backend = is_emergency;
1428+
result = RT_EOK;
1429+
}
1430+
1431+
rt_mutex_release(&ulog.output_locker);
1432+
1433+
return result;
1434+
}
1435+
13691436
#ifdef ULOG_USING_ASYNC_OUTPUT
13701437
/**
13711438
* asynchronous output logs to all backends
13721439
*
1440+
* @param[in] emergency_mode A flag to select the operational mode. RT_FALSE for
1441+
* normal operation, RT_TRUE for emergency fault context.
1442+
*
13731443
* @note you must call this function when ULOG_ASYNC_OUTPUT_BY_THREAD is disable
13741444
*/
1375-
void ulog_async_output(void)
1445+
void ulog_async_output(rt_bool_t emergency_mode)
13761446
{
13771447
rt_rbb_blk_t log_blk;
13781448
ulog_frame_t log_frame;
@@ -1387,14 +1457,12 @@ void ulog_async_output(void)
13871457
log_frame = (ulog_frame_t) log_blk->buf;
13881458
if (log_frame->magic == ULOG_FRAME_MAGIC)
13891459
{
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);
1460+
_ulog_output_to_backends(emergency_mode, log_frame->level, log_frame->tag, log_frame->is_raw, log_frame->log, log_frame->log_len);
13931461
}
13941462
rt_rbb_blk_free(ulog.async_rbb, log_blk);
13951463
}
13961464
/* output the log_raw format log */
1397-
if (ulog.async_rb)
1465+
if (ulog.async_rb && !emergency_mode)
13981466
{
13991467
rt_size_t log_len = rt_ringbuffer_data_len(ulog.async_rb);
14001468
char *log = rt_malloc(log_len + 1);
@@ -1434,14 +1502,14 @@ rt_err_t ulog_async_waiting_log(rt_int32_t time)
14341502

14351503
static void async_output_thread_entry(void *param)
14361504
{
1437-
ulog_async_output();
1505+
ulog_async_output(RT_FALSE);
14381506

14391507
while (1)
14401508
{
14411509
ulog_async_waiting_log(RT_WAITING_FOREVER);
14421510
while (1)
14431511
{
1444-
ulog_async_output();
1512+
ulog_async_output(RT_FALSE);
14451513
/* If there is no log output for a certain period of time,
14461514
* refresh the log buffer
14471515
*/
@@ -1471,7 +1539,7 @@ void ulog_flush(void)
14711539
return;
14721540

14731541
#ifdef ULOG_USING_ASYNC_OUTPUT
1474-
ulog_async_output();
1542+
ulog_async_output(RT_FALSE);
14751543
#endif
14761544

14771545
/* flush all backends */
@@ -1485,6 +1553,36 @@ void ulog_flush(void)
14851553
}
14861554
}
14871555

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

components/utilities/ulog/ulog.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ rt_err_t ulog_backend_register(ulog_backend_t backend, const char *name, rt_bool
5454
rt_err_t ulog_backend_unregister(ulog_backend_t backend);
5555
rt_err_t ulog_backend_set_filter(ulog_backend_t backend, ulog_backend_filter_t filter);
5656
ulog_backend_t ulog_backend_find(const char *name);
57+
rt_err_t ulog_backend_set_emergency(const char *name, rt_bool_t is_emergency);
5758

5859
#ifdef ULOG_USING_FILTER
5960
/*
@@ -74,12 +75,13 @@ const char *ulog_global_filter_kw_get(void);
7475
* flush all backends's log
7576
*/
7677
void ulog_flush(void);
78+
void ulog_emergency_flush(void);
7779

7880
#ifdef ULOG_USING_ASYNC_OUTPUT
7981
/*
8082
* asynchronous output API
8183
*/
82-
void ulog_async_output(void);
84+
void ulog_async_output(rt_bool_t emergency_mode);
8385
void ulog_async_output_enabled(rt_bool_t enabled);
8486
rt_err_t ulog_async_waiting_log(rt_int32_t time);
8587
#endif

components/utilities/ulog/ulog_def.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ struct ulog_backend
198198
char name[RT_NAME_MAX];
199199
rt_bool_t support_color;
200200
rt_uint32_t out_level;
201+
rt_bool_t is_emergency_backend; /**< Can this backend be called safely in a fault/emergency context? */
201202
void (*init) (struct ulog_backend *backend);
202203
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);
203204
void (*flush) (struct ulog_backend *backend);

src/scheduler_comm.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ void rt_scheduler_stack_check(struct rt_thread *thread)
485485
rt_err_t hook_result = -RT_ERROR;
486486

487487
LOG_E("thread:%s stack overflow\n", thread->parent.name);
488+
ulog_emergency_flush();
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)