5a9988b557
清掉的(读者无从解析、且所指文档已不存在) - 已删任务文档的编号:Task #32、其内部 §5 / §4.3、P1-5、P0-2、P1-4a、 "方案 a"、"Lead 批准 2026-10-12" - 正文里当代词用的文件版本号:"(v2.7 起为 AboveNormal)""已随 v2.0 事件 驱动改造删除""(v2.0:DMA 消费)""见文件头 v1.3 说明"等 → 改写成陈述现状 - 测试与脚本同类:adc_input 桩头"v2.0:DMA 模型"、stub/adc.h 的 v2.0、 check_cubemx_survival.py 段头的"2026-10-10 v2.8"、run_all_suites.py 的"方案 A" 刻意保留 - 可检索登记簿锚点:P-AUDIT 真条目(P0-1/P1-8/P2-1/P2-9/P3-11)、S6/S11、 E-31/E-44、N-18/N-23/N-32、B1/B5/B7/B8、L1-x~L5-x 功能号 - 文件头修改记录块内的 vN.M(规范 V1.0 §2.1/§2.2 要求保留该块;块内编号 在本文件内自洽可读) 顺带纠正注释与代码已经不一致的过时事实 - 仍把 TIM6 写成现行节拍源:Src/main.c 的 include 注释与回调注释、 Src/freertos.c 的 adcTask 块 → 改 TIM3;adc_input.h 修订条目里"ADC1 只认 TIM2/TIM3/TIM8 的 TRGO"不准确 → 换成指向判据(ADC采样节拍设计说明 §3.2) - App/algo/phasor.c 双区注释称"唯一消费者是 adcTask、为后续保护任务预留", 实际 protectTask 已跨上下文读(prot_elm_run -> phasor_get)→ 改为"读者跨 adcTask 心跳与 protectTask 判据两个上下文,勿改加锁、勿取消双区",并同步 修正 docs/算法/采集与算法层设计说明.md 同一句 - adc_tick_irq_notify() 的"拒绝 ISR 内计算"理由仍写"内含 2 ms 忙等"(DMA 化 后已无忙等)→ 换成三条真实理由:任务级共享数据 / 计算量 / 生成区回调 - 裸 §5(指代不明)与"规划 §1.6 / 规划 §4"昵称式 → 写成完整路径紧邻式 字节卫生(Edit 会静默剥 BOM) - App/task/ui_task.c 与 Drivers/BSP/UI/ui_net_sink.c 被剥掉 BOM 两次,均按 3 字节 EF BB BF 前缀补回并复验;全部 20 个文件 CR=0、U+FFFD=0、无行尾空白 门禁 - @build 0 错误 0 警告,且 Program Size 与清理前逐项相同 (Code=213356 RO=18580 RW=1184 ZI=131480)→ 纯注释改动、中文未被误重编码 - ui-golden 首跑红:新工作树缺被 gitignore 的黄金参考 simulator/tests/ref/ (不入库、只在主工作树生成过);从主工作树复制该基线后复跑通过 → 注释改动 零像素影响 - 全套 pre-commit 在这 20 个文件上 Passed(coding-standard / cppcheck / 6 个主机单测 / doc-links / task-table / mem-numbers / cubemx-survival / registration-check / mem-budget / font-charset / ui-golden / id-check) - id-check 可判定比例 82.71% -> 82.88%(删掉的是"形似 P-AUDIT 但查无定义"的 假锚点),悬空引用 863 -> 854
141 lines
7.3 KiB
C
141 lines
7.3 KiB
C
/*
|
||
* 模块名称:Task Profiler(任务耗时最坏值探针)
|
||
* 模块功能:用内核 DWT 周期计数器(CYCCNT,168 MHz 主频下 168 周期 = 1 us)为
|
||
* 各任务主循环的**最坏耗时 / 执行次数 / "此刻是否仍在某段内"** 提供
|
||
* 常开、低开销的观测面,经控制台单写者输出 `[PROF]` 行。
|
||
* 用途:把"某任务心跳缺席"这一结果拆成三种互斥机理,供归因定位。
|
||
* 适用平台:STM32F407ZGTx(仅 MCU 端:依赖 DWT 与 HAL 时基;模拟器/主机单测
|
||
* 不编译本模块)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-10-10
|
||
* 修改记录:
|
||
* v1.0 2026-10-10 王建锋 创建:常开任务耗时探针
|
||
*
|
||
* 设计背景(docs/子系统设计/看门狗设计说明.md §11.4):
|
||
* uiTask 偶发心跳缺席导致 IWDG 复位,但"占用者"无法从既有日志判定。
|
||
* 同一现象至少对应三种互斥机理,观测上必须能区分:
|
||
* ① 被调度饿死 → 循环周期长,而段内耗时短;
|
||
* ② 阻塞在网络往返 → `ui_net_sink_poll()` 段内耗时长;若永久阻塞,
|
||
* 该段样本**永不结算**,只有"此刻仍在段内已 X us"能证明;
|
||
* ③ 自身渲染拖长 → 单轮工作耗时长。
|
||
* 故每个探针记录 max / count / last,并对可疑段记录 enter/exit。
|
||
*
|
||
* ⚠ 开销:一次采样 = 1 次 DWT 读 + 数次比较/存储(< 20 周期);对 1 ms 节拍
|
||
* (168000 周期)可忽略。无动态分配、无内核对象、无临界区、无阻塞。
|
||
* ⚠ 单写者假设:同一探针 ID 只由**一个任务**写入(见下表),报告由
|
||
* defaultTask 读取。32 位对齐读写在 Cortex-M4 上原子,读侧不会撕裂。
|
||
*
|
||
* 探针 ID 写入者 含义(enter/exit = 可读出"此刻仍在段内已多久")
|
||
* UI_PERIOD uiTask 上轮循环起点 → 本轮循环起点(enter/exit)
|
||
* UI_WORK uiTask 本轮起点 → 心跳前(含 LVGL 渲染 + 推流轮询)
|
||
* UI_SINK uiTask ui_net_sink_poll() 单次往返(enter/exit)
|
||
* ADC_BEAT adcTask 单拍起点 → 心跳后(含取数 + 压环 + 相量 + 心跳日志)
|
||
* ADC_POLL adcTask adc_input_poll() 单次(enter/exit)——隔离 ADC 超时成本
|
||
* DEF_DRAIN defaultTask dbg_console_drain()(enter/exit)——控制台忙等发送
|
||
* DEF_SYSLOG defaultTask syslog_drain()(enter/exit)——NAND/FTL 写
|
||
* NET_POLL netTask net_poll()(enter/exit)——CH395F 驱动轮询
|
||
*
|
||
* 后四个探针用于**指认占用者**:UI 心跳缺席时,谁的最坏耗时在同一窗口抬起来,
|
||
* 谁就是挤占 CPU 的 Normal 任务(见 docs/子系统设计/看门狗设计说明.md §11.2 第 4 步)。
|
||
*/
|
||
|
||
#ifndef TASK_PROF_H
|
||
#define TASK_PROF_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* 探针 ID(顺序即内部槽位下标,新增只能追加) */
|
||
typedef enum {
|
||
TASK_PROF_UI_PERIOD = 0, /* uiTask 循环周期 */
|
||
TASK_PROF_UI_WORK = 1, /* uiTask 单轮工作耗时(不含 osDelay) */
|
||
TASK_PROF_UI_SINK = 2, /* ui_net_sink_poll() 往返耗时 */
|
||
TASK_PROF_ADC_BEAT = 3, /* adcTask 单拍耗时 */
|
||
TASK_PROF_ADC_POLL = 4, /* adc_input_poll() 耗时(隔离 ADC 超时成本) */
|
||
TASK_PROF_DEF_DRAIN = 5, /* defaultTask 控制台排空耗时 */
|
||
TASK_PROF_DEF_SYSLOG = 6, /* defaultTask syslog 落盘耗时 */
|
||
TASK_PROF_NET_POLL = 7, /* netTask net_poll() 耗时(CH395F 驱动) */
|
||
TASK_PROF_ID_COUNT
|
||
} task_prof_id_t;
|
||
|
||
/*
|
||
* 函数功能:读 DWT 周期计数器(懒使能;基准为"任意",仅供取差值)
|
||
* 入口参数:无
|
||
* 返回值:当前 CYCCNT 值(周期数)
|
||
* 限定条件:任意上下文可调(幂等;纯寄存器读,不创建任何内核对象)
|
||
* 函数说明:首次调用置 CoreDebug->DEMCR.TRCENA 与 DWT->CTRL.CYCCNTENA。
|
||
* 刻意**不清零** CYCCNT:本模块只用差值,清零无意义,且会与
|
||
* console_log.c 的同名惰性使能互相干扰。DWT 使能失败时恒返回 0,
|
||
* 采样侧把 0 判为"不可用"(见 task_prof_sample)。
|
||
*/
|
||
uint32_t task_prof_cyc(void);
|
||
|
||
/*
|
||
* 函数功能:记录一次耗时样本(周期数)
|
||
* 入口参数:id - 探针 ID
|
||
* cyc - 耗时(周期数;0 或超出合理上界视为不可用/计数器被重置)
|
||
* 返回值:无
|
||
* 限定条件:单写者(每个 ID 只由一个任务调用)
|
||
* 函数说明:更新 count / last / max。`cyc == 0`(DWT 未使能)或
|
||
* `cyc >= TASK_PROF_GLITCH_CYC`(计数器中途被重置造成的无符号回绕)
|
||
* 只累加 glitch 计数、不污染 max——否则一次计数器重置就会把
|
||
* 最坏值抬到 ~2^32 周期,掩盖真实数据。
|
||
*/
|
||
void task_prof_sample(task_prof_id_t id, uint32_t cyc);
|
||
|
||
/*
|
||
* 函数功能:标记"进入某段"(用于观测"此刻仍在段内已多久")
|
||
* 入口参数:id - 探针 ID
|
||
* 返回值:无
|
||
* 限定条件:单写者;须与 task_prof_exit 成对
|
||
* 函数说明:记录进入时刻并置"在段内"标志。若该任务此后永久阻塞在该段,
|
||
* 样本永不结算,只有报告侧的"仍在段内"字段能证明它卡在哪里。
|
||
*/
|
||
void task_prof_enter(task_prof_id_t id);
|
||
|
||
/*
|
||
* 函数功能:结算一段耗时(与 task_prof_enter 成对)
|
||
* 入口参数:id - 探针 ID
|
||
* 返回值:无
|
||
* 限定条件:单写者;须与 task_prof_enter 成对
|
||
* 函数说明:清"在段内"标志 → 以 enter 时刻为起点记一次样本。
|
||
* **未 enter 过(in_seg == 0)时直接返回**——这使"先 exit 再 enter"的
|
||
* 周期测量写法(如 uiTask 的循环周期)在首轮不会拿 0 当起点,
|
||
* 从而不产生一次虚假的超大样本。
|
||
*/
|
||
void task_prof_exit(task_prof_id_t id);
|
||
|
||
/*
|
||
* 函数功能:周期性打印 `[PROF]` / `[PROF2]` 行(内部节流:首次 5 s 后,此后每 2 s)
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:仅 defaultTask 主循环调用(唯一串口写者;静态行缓冲复用)
|
||
* 函数说明:整行一次 `dbg_console_write_line` 入环形缓冲(行级原子)。
|
||
* `[PROF]` = uiTask/adcTask 任务级(周期 / 工作 / 往返 / 单拍);
|
||
* `[PROF2]` = 占用者段级(defaultTask 排空与落盘、netTask 驱动轮询、
|
||
* adcTask ADC 单次),用于指认"谁把 CPU 占住了"。
|
||
* 用**静态**行缓冲而非栈缓冲:本函数挂在 defaultTask 的最坏调用链上,
|
||
* 栈上再放一个 192 B 行缓冲会挤占该任务的栈预算。
|
||
*/
|
||
void task_prof_report(void);
|
||
|
||
/*
|
||
* 函数功能:停摆现场快照——同 `[PROF]` + `[PROF2]` 两行,走**裸 UART 直发**
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:仅 defaultTask 调用(此时它就是唯一串口写者,无并发)
|
||
* 函数说明:用于"某任务心跳刚由'到过'变为'缺席'"的瞬间。缺席窗口内环形缓冲
|
||
* 可能积压,裸直发保证这两行能出去(与 task_wd_poll 的 `MISS start`
|
||
* 标记同款理由)。
|
||
*/
|
||
void task_prof_report_raw(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* TASK_PROF_H */
|