Files
STM32F4-Base/Drivers/BSP/app_assert.h
T
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名,
不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体
run_boot_test.py 的真实调用。

- AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改
  「编译(/keil-build)」「串口回归(/serial-boot-test)」
- keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree
  方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围)
- 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入
  等 41 行历史证据保留旧名
- gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑
- 顺手补并行方案同行漏网计数 51→52 钩子

验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all
rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52
钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00

131 lines
7.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:模块级轻量断言宏(APP_ASSERT)
* 模块功能:给自研代码里"不该发生"的**内部不变量**一个统一出口——编译期整体
* 裁剪,生产构建零符号零开销;打开后与 configASSERT 共用同一个失败钩子。
* 规格出处:本头注释即规格正文(原专项文档已删除,2026-10-28;历史见 git log)。
* 早期示意稿自称"示意,非最终实现",本文件即落地实现;与示意稿的两处
* 差异见下方"落地差异"。
* 落地差异(相对早期示意稿,均为有意):
* 1. 断言点**不再额外发一条 DBG_ERROR**:钩子本身就打印 file:line(断言唯一有
* 价值的现场信息),而每条 DBG_* 调用点约 90 B Flash + 216 B 栈(AGENTS
* 「日志不是免费的」);断言点又常处在"内核状态已不可信"的现场,走裸控制台
* 比走日志行缓冲更稳。
* 2. 本头落在 Drivers/BSP 根目录(与 dbg_log.h、bsp_err.h 同级),不是示意稿写的
* App/util:它是纯宏叶子能力(无状态、无数据、不依赖任何上层服务),BSP 与
* App 两侧都要用。放 App/util 会让 BSP 层新增一条"反向 include App 层头"的边,
* 而 layer-deps 门禁的存量基线只减不增,加不进去。落在 Drivers/BSP 已在 MCU
* (uvprojx IncludePath)与模拟器(simulator/CMakeLists.txt 的
* include_directories)双端 include 路径内 → **双端零工程文件改动**;主机侧
* 单测也因此直接用真头、不再造桩——假件与真头漂移正是"常量闸"要防的那类
* 自洽假绿(见 test/host/net_socket/run_unit_test.py 的注释)。
* ⚠ 本注释块内禁止出现"斜杠紧跟星号"的连写(Markdown 粗体收尾在路径末尾时最容易
* 撞上,例如目录通配那种写法):ARMCC 报 #9-D nested comment is not allowed,
* 而 /keil-build 的验收口径是 0 错误 **0 警告**(2026-10-25 首编即中招,已改)。
* 适用平台:STM32F407ZGTx(ARMCC v5,C99);PC 模拟器与主机侧单测(MSVC)同标准
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* 2026-10-25 王建锋 创建(S12;试点 = Drivers/BSP/NET/net_socket.c 状态机不变量 2 处)
*/
#ifndef APP_ASSERT_H
#define APP_ASSERT_H
#ifdef __cplusplus
extern "C" {
#endif
/* ============================================================================
* 一、编译期裁剪分级:两态(规格只给"生产关 / 调试开",不设中间档)
* ==========================================================================*/
/*
* 开关:**默认关闭 = 产品构建**。
* 打开方式 = 取消下一行注释。策略与 test/board/test_config.h 的
* ENABLE_DESTRUCTIVE_TESTS 同级:默认必须关闭,严禁以打开态提交。
* ⚠ 关闭态是 /keil-build 的验收口径:"0 错误 0 警告" 且 .map 无新增符号
* (规格 §2.1 完成判据)。本宏不新增任何 .c,故不涉及双端注册。
*/
//#define APP_ASSERT_ENABLE
/* ============================================================================
* 二、打开态:失败交"与 configASSERT 同一个出口"
* ==========================================================================*/
#ifdef APP_ASSERT_ENABLE
#if defined(__arm__) || defined(__CC_ARM)
/*
* MCU 端:Inc/FreeRTOSConfig.h 的 configASSERT 用的就是这个钩子——实现见
* Src/freertos.c 的 USER CODE Application 区(裸控制台打
* "[ERR] ASSERT FAILED <file>:<line>" 后 NVIC_SystemReset())。
* 应用层断言复用同一钩子 → 台架上"断言是怎么死的"只有一种形态,不必再造
* 第二条复位路径。这里**不** include FreeRTOSConfig.h:本头必须能被看不见
* RTOS 的编译单元包含(模拟器 / 主机侧单测就是这种)。声明与
* FreeRTOSConfig.h 原文同签名——重复声明合法,真漂移了由链接器兜底。
*/
extern void vApplicationAssertFailed(const char *file, long line);
#define APP_ASSERT_FAIL(p_file, line) vApplicationAssertFailed((p_file), (long)(line))
#else
/*
* 宿主端(PC 模拟器 / 主机侧单测):没有 RTOS、也没有可复位的外设看门狗,
* 正确形态是"现场写 stderr 然后 abort()"——非零退出码,任何一键回归都会
* 判失败,不会被"静默继续"把断言吞成一条日志。
*/
#include <stdio.h>
#include <stdlib.h>
#define APP_ASSERT_FAIL(p_file, line) do { \
(void)fprintf(stderr, "[ASSERT] FAILED %s:%d\r\n", (p_file), (int)(line)); \
(void)fflush(stderr); \
abort(); \
} while (0)
#endif /* __arm__ || __CC_ARM */
/*
* 断言宏(打开态):条件为假 → 交 APP_ASSERT_FAIL 处置。
* 包成 do { } while (0) 以适配"单条语句"位置;条件只求值一次。
*/
#define APP_ASSERT(cond) do { \
if (!(cond)) { \
APP_ASSERT_FAIL(__FILE__, __LINE__); \
} \
} while (0)
/* ============================================================================
* 三、关闭态(默认):代码消失,但表达式的类型检查保留
* ==========================================================================*/
#else /* !APP_ASSERT_ENABLE */
/*
* 与示意稿的 ((void)0) 同样"零代码零符号",但用 sizeof 把条件留在编译期的
* 类型检查里:sizeof 的操作数是**不求值上下文**(不生成代码、不产生副作用),
* 于是断言写错变量名 / 写错比较类型时,生产构建也会当场报错——否则"生产裁掉
* 断言"等于"这条表达式从没被编译器看过",要等到哪天有人打开开关才炸。
* 附带好处:只在断言里出现的变量不会被 ARMCC 判成"set but never used"。
*/
#define APP_ASSERT(cond) do { (void)sizeof(!!(cond)); } while (0)
#endif /* APP_ASSERT_ENABLE */
/* ============================================================================
* 四、使用纪律(规格 §2.1 的三条 + 落地补两条;错用是常态,故随头常驻)
* ==========================================================================*/
/*
* 1. **不得**在中断上下文或高频路径使用——打开态每次失败都要打印并复位;
* 本宏服务"逻辑上不可能",不是"偶尔会失败"。
* 2. **不得**替代正常错误返回:外部输入、对端数据、芯片返回值一律走错误码
* (约定见 Drivers/BSP/bsp_err.h 与 net_* 的返回契约)。断言只写内部不变量
* ——即"为假就说明我们自己写错了"的状态机 / 指针来源 / 字段配对关系。
* 3. 生产构建关闭(与 ENABLE_DESTRUCTIVE_TESTS 同级):要开就在本地或测试构建
* 里打开,**跑完必须还原成注释态**。
* 4. 断言条件**不得带副作用**:关闭态不求值、打开态才求值,带副作用等于让行为
* 随开关漂移——这是本宏唯一真正危险的误用形态。
* 5. 台架口径:MCU 钩子打印以 [ERR] 开头 → 真机上断言一旦触发,/serial-boot-test 必然判本次
* 失败,这是刻意的(AGENTS「Forbidden 标记含 [ERR]」/ 出现 Fault 即不通过)。
*/
#ifdef __cplusplus
}
#endif
#endif /* APP_ASSERT_H */