栈整改不抬栈:删旧档迁移、FIL静态化、日志行缓冲按模块收紧、钩子脱离HAL,defaultTask链5312→2776B

This commit is contained in:
2026-10-10 10:17:00 +08:00
parent 5c978465c9
commit 0779d3a075
54 changed files with 3203 additions and 2770 deletions
+70 -4
View File
@@ -36,7 +36,9 @@
#include "task.h"
#include "cmsis_os.h"
#include "sys_events.h"
#include "sys_log.h"
/* 刻意不 include "sys_log.h":日志子系统不得回调本模块(调用图成环 →
* armlink 静态栈深在每个任务上虚高,见 dbg_console_drain 故障路径注释)。
* TX 故障留痕改由写者任务在 drain 之外补记 */
/* ===== 控制台单写者日志(2026-09-24 加固,HardFault 定位结论驱动)=====
* 背景:旧路径 DBG_* → printf → fputc **每字符一次** HAL_UART_Transmit。HAL 的锁只保证
@@ -66,6 +68,17 @@
* IWDG 误复位(round 8a 台架现场)。改用 DWT CYCCNT(内核硬件周期计数,不依赖任何
* 中断/uwTick)做判据;DWT 使能失败时退回 HAL_UART_Transmit(旧行为,功能不退化)。 */
#define CON_TX_TIMEOUT_CYC (SystemCoreClock / 10U) /* 发送硬上界 = 100ms 对应周期数 */
/*
* DWT 不可用时的**指令计数**上界(替代 HAL_UART_Transmit 回退,2026-10-10)。
* 为什么不再回退 HAL:① HAL_UART_Transmit 的超时判据是 HAL_GetTick——断言/停摆
* 现场 tick 未必在走,等待无上界;② 它的调用链 `HAL_UART_Transmit →
* UART_WaitOnFlagUntilTimeout` 会被 armlink 挂进**每个**碰日志的任务的最坏链
* (configASSERT 在 FreeRTOS 内部可达 → 三个内核钩子都在链尾),实测每条链多背
* 64 B 栈。计数上界同样有界、零外部依赖。
* 取值:115200 bps 下一字节 87 µs;轮询循环 ~5 周期 → 每字节约 3000 次迭代,
* 取 20000 留 6 倍余量(单字节卡死最多数毫秒级即放弃,不无限自旋)
*/
#define CON_TX_GUARD_MAX 20000u
#define CON_APPEND_WAIT_CYC (SystemCoreClock / 5U) /* 缓冲满等待硬上界 = 200ms 对应周期数 */
static uint8_t s_con_ring[DBG_CON_RING_SIZE];
@@ -73,6 +86,7 @@ static volatile uint16_t s_con_head; /* 生产者写入位置 */
static volatile uint16_t s_con_tail; /* 写者读取位置 */
static volatile uint32_t s_con_drop; /* 因缓冲满丢弃的行数 */
static volatile uint32_t s_con_tx_fault; /* TX 失败(僵尸通道)次数,均经外设软复位自愈 */
static volatile uint8_t s_con_tx_fault_log; /* TX 故障留痕待上报(由写者任务在 drain 之外补记 run.log) */
static volatile uint32_t s_con_cool_until; /* 故障冷却截止 tick(0 = 无冷却) */
static volatile uint8_t s_con_draining; /* 1 = 处于 drain 中(重入保护) */
static volatile uint8_t s_con_writer_up; /* 1 = 写者已注册(此后走环形缓冲) */
@@ -109,13 +123,45 @@ static void con_dwt_ensure(void) {
}
}
/*
* 函数功能:DWT 不可用时的计数上界发送(每字节/TC 各数 CON_TX_GUARD_MAX 次)
* 入口参数:u - USART 寄存器基址(调用方已判非 NULL) USART_TypeDef *
* p_buf - 待发数据;len - 字节数
* 返回值:HAL_OK - 已发出;HAL_TIMEOUT - 计数上界内未完成(本块放弃)
* 限定条件:USART1 已初始化
* 函数说明:刻意不用 HAL_UART_Transmit——理由见 CON_TX_GUARD_MAX 处注释
* (tick 依赖不可信 + 其调用链污染所有任务的最坏栈链)
*/
static HAL_StatusTypeDef con_tx_counted(USART_TypeDef *u, const uint8_t *p_buf, uint16_t len) {
uint32_t guard;
uint16_t i;
for (i = 0U; i < len; i++) {
guard = 0U;
while ((u->SR & USART_SR_TXE) == 0U) {
if (++guard >= CON_TX_GUARD_MAX) {
return HAL_TIMEOUT;
}
}
u->DR = (uint16_t)p_buf[i];
}
guard = 0U;
while ((u->SR & USART_SR_TC) == 0U) {
if (++guard >= CON_TX_GUARD_MAX) {
return HAL_TIMEOUT;
}
}
return HAL_OK;
}
/*
* 函数功能:有界裸发送(不依赖 uwTick / HAL 超时)
* 入口参数:p_buf - 待发数据;len - 字节数(调用方已限 ≤ DBG_CON_CHUNK_MAX)
* 返回值:HAL_OK - 已全部写入发送寄存器;HAL_TIMEOUT - 有界上界内未完成
* 限定条件:USART1 已初始化
* 函数说明:寄存器级轮询 TXE/TC,超时判据用 DWT CYCCNT(CON_TX_TIMEOUT_CYC);
* DWT 未使能时退回 HAL_UART_Transmit(旧行为)。本函数只做 TX,
* DWT 未使能时走 con_tx_counted(指令计数上界,2026-10-10 取代
* HAL_UART_Transmit 回退)。本函数只做 TX,
* 不触碰 RX/错误标志——僵尸通道自愈由调用方按返回值处理。
* ⚠ USART1 未初始化(huart1.Instance == NULL,如 HAL_Init 早期或
* 断言/故障上报路径)时立即返回 HAL_ERROR:否则会解引用 NULL 去读写
@@ -131,7 +177,7 @@ static HAL_StatusTypeDef con_tx_bounded(const uint8_t *p_buf, uint16_t len) {
return HAL_ERROR; /* USART1 未初始化:无可用通道,安全让位 */
}
if (s_con_dwt_up == 0U) {
return HAL_UART_Transmit(&huart1, (uint8_t *)p_buf, len, DBG_CON_TX_TIMEOUT_MS);
return con_tx_counted(huart1.Instance, p_buf, len);
}
u = huart1.Instance;
@@ -254,7 +300,14 @@ void dbg_console_drain(void) {
if (s_con_tx_fault == 1U) {
static const char note[] = "\r\n[WARN] CONSOLE tx fault: UART reinit (self-healed)\r\n";
syslog_post(SYSLOG_RUN, "console tx fault, uart reinit"); /* 仅 run.log,不入自检判定 */
/* 刻意**不在这里**调 syslog_post():syslog_post → dbg_console_write_line
* → con_append → dbg_console_drain 会与本函数构成**调用图成环**。运行时
* 有 s_con_draining 重入标志挡着,不会真递归;但 armlink 静态调用图会把
* 整条日志尾巴在每个任务的最坏链上多叠一层(实测 6 个任务的最坏链末尾
* 全是这个环)→ 栈深数字虚高、真实风险反而被掩盖。
* 改法:只置标志,由写者任务(defaultTask 主循环,本就是 syslog 唯一
* 写者)在 drain **之外**补记 run.log(见 dbg_console_tx_fault_log_take) */
s_con_tx_fault_log = 1U;
(void)dbg_console_write_raw(note, (unsigned)sizeof(note) - 1U);
}
break; /* 故障即止:本轮 drain 到此为止,让位主循环 */
@@ -401,6 +454,19 @@ uint32_t dbg_console_tx_fault(void) {
return s_con_tx_fault;
}
/*
* 函数功能:取走"控制台 TX 故障待上报"标志(读后清)
* 返回值:1 - 需要补记一条 run.log 证据,0 - 无
* 限定条件:**写者任务**(defaultTask)在 dbg_console_drain() 之外调用
* 函数说明:断调用图成环用,见 dbg_console_drain 故障路径注释
*/
uint8_t dbg_console_tx_fault_log_take(void) {
uint8_t pending = s_con_tx_fault_log;
s_con_tx_fault_log = 0U;
return pending;
}
/*
* 函数功能:MicroLIB printf 底层重定向(库内部/遗留 printf 调用)
* 函数说明:同样只把字节交给单写者路径;未注册写者时直发(与旧行为一致)