1. apps/chrg/debug_hot.ini: 增加调试断点配置 2. apps/chrg/applications/thread/chrg_north.h: 开启北向调试日志 3. 修正多个头文件函数返回值类型,从void改为int 4. 重构chrg_thread.c: 新增mb消息释放函数,优化邮箱发送逻辑 5. 重写chrg_south.c: 修复分包越界和内存泄漏,增加帧超时处理 6. 重构chrg_sink.c: 统一批量下发接口,修正初始化和运行逻辑 7. 重写chrg_comm.c: 修复系统寄存器写入逻辑,增加通道激活校验 8. 重构chrg_north.c: 修复北向分包越界和内存泄漏,增加超时处理和诊断命令 9. 重构chrg_roll_sou.c: 重构批量下发逻辑,增加重试机制和错误处理
6.9 KiB
静态排查后,暂未看到明确的 ABBA 型互斥锁环。当前现象更像“内存破坏/资源泄漏后线程停滞”或“状态机无限重试”,而不是整个 RTOS 调度器死锁。
另外,代码中使用的是 STM32 内部 IWDG,不是外部独立看门狗。
可疑点
- 高风险:南北向分包接收存在确定的越界写和泄漏路径
len_need = len_msg - 帧头长度
但此时 pFrame->length 已经包含之前收到的数据,正确的剩余长度应基于 len_msg - pFrame->length。当前代码却从 payload[pFrame->length] 开始写入整个“帧头后的总长度”。
例如总长 21 字节,首批已存 8 字节,下一次仍可能从偏移 8 写入 18 字节,最终写到偏移 26,越过 21 字节堆块。这会破坏相邻堆块、邮箱消息甚至 RT-Thread 内核对象,最终表现为随机线程卡死、互斥量异常或堆分配失败。
同时,分包超时路径只清除了 findhead:
没有释放 pMB/payload,也没有清除 length/len_msg。而且没有新数据时会提前 continue,超时检查根本不会执行。串口噪声、丢字节或正常分包都可能不断积累泄漏。
此外,payload 分配失败时只返回,没有释放刚分配的 pMB:
这是目前最符合“运行时间越长越容易出现”的可疑点。
- 高风险:看门狗喂法无法覆盖业务线程死锁
主线程每秒无条件喂狗:main.c:36。
北向轮询线程又在每轮无条件喂同一只狗:chrg_roll_nor.c:421。
因此即使 thr.comm、thr.sou、thr.rollsou、LCD 线程全部停止工作,只要主线程或北向轮询线程仍能调度,IWDG 就永远不会复位。这只是在检测“CPU/调度器是否完全停止”,没有检测业务线程是否健康。
驱动最终调用的是 HAL_IWDG_Refresh():drv_wdt.c:40。代码中没有找到外部 WDI 引脚翻转或外部看门狗驱动。
- 高风险:当前新增的南向批处理可能永久停在
RUN_COM
南向批处理遇到 CRC 错误或应答超时后,不增加重试次数、不设置截止时间,也不清除任务:
- CRC 错误:chrg_roll_sou.c:608
- 应答超时:chrg_roll_sou.c:627
线程会一直重发同一个寄存器,pending 永远不清除,run 永远保持 RUN_COM,正常南向轮询无法恢复。这属于业务状态机死锁,调度器仍正常运行,所以看门狗不会复位。
这套批处理逻辑是当前工作区相对 HEAD 新增的,优先级很高。
- 高风险:南向共享状态的互斥保护实际无效
chrg_set_sou_reg 先在锁外写入:
pROLL->ch
pROLL->reg
pROLL->value
随后互斥量只保护 pROLL->run。消费者也只在锁内读取 run,实际使用 ch/reg/value 时已经离开锁。
多个生产者包括 LCD、Modbus 和状态机可能交叉覆盖这些字段,形成“通道来自命令 A、寄存器来自命令 B、值来自命令 C”的组合。
批处理数组 g_sou_com_batch 同样由生产者和 thr.rollsou 同时读写,却没有共同的锁:chrg_roll_sou.c:394。
- 中高风险:先切换
RUN_COM,再填充批处理数据
chrg_sink_diff_send_reg 先调用 chrg_sou_com_sou_set() 将状态切到 RUN_COM,然后才追加寄存器。
chrg_sink_work() 也是相同顺序:chrg_sink.c:541。
如果消费者在状态切换后看到尚未填充的批次,会认为没有任务并切回自动模式;随后加入的 pending 数据可能一直无人处理。当前线程优先级降低了部分窗口概率,但没有从同步机制上消除竞态。
- 中高风险:应答邮箱没有请求、通道或序号关联
邮箱消息只有 length + payload:chrg_def.h:95。
南北轮询线程默认“收到的下一帧就是刚才请求的响应”。南向批处理只检查 CRC,任何延迟到达的旧响应都可能让当前批次 cur_idx++:chrg_roll_sou.c:574。
在超时、重发、通道切换或邮箱积压后,请求与响应很容易错位,最终造成状态机停在错误步骤或无限重试。
- 中风险:所有业务互斥量都是永久等待
TTY、南北轮询互斥量统一使用 RT_WAITING_FOREVER。例如:
目前没有发现确定的反向加锁环,但一旦锁所有者因堆破坏、状态机卡死或设备写阻塞而不再释放,其他线程会永久挂起,没有超时和故障上报。
- 中风险:串口 RingBuffer 写入结果被忽略
chrg_north.c:623 和 chrg_south.c:383 没有检查 rt_ringbuffer_put() 实际写入长度。
缓冲区满时会静默丢字节,随后触发分包超时、泄漏和上述越界路径,是长期运行后出现问题的重要放大器。
优先判断
最可能的两条故障链是:
- 串口分包 → 长度计算错误/超时泄漏 → 堆损坏或耗尽 → 某些业务线程永久等待;主线程继续喂狗。
- 南向某条命令超时或响应错位 →
RUN_COM无限重发 → 南向业务永久停止;主线程和北向线程继续喂狗。
现场故障尚能进入 Shell 时,优先保存 list_thread、list_mutex、list_mailbox、free 输出,并检查是否持续打印 com batch timeout, resend current reg。这几项可以直接区分锁等待、堆耗尽和状态机无限重试。
未修改任何文件。