软件诊断 Cluster(SoftwareDiagnostics)

Cluster ID: 0x0034  |  所在 Endpoint: Endpoint 0(Root / Node 级别) |  角色: Server(只读 + 一个重置命令)

SoftwareDiagnostics 用于暴露设备固件的运行时健康状态 —— 包括线程栈使用情况、堆内存分配和软件故障记录。 这是 Matter 中面向开发者和运维人员的诊断 Cluster,帮助在不连接调试器的情况下了解嵌入式设备的内部状态。

什么时候用

设备运行一段时间后行为异常?读取堆内存属性检查是否存在内存泄漏。 怀疑某个线程栈溢出?查看 ThreadMetrics 中的 StackFreeMinimum。 OTA 升级后想确认固件稳定性?监控 SoftwareFault 事件和高水位线变化。 这些信息在产品量产后的远程诊断中尤其有用。

Feature 位图

SoftwareDiagnostics Cluster 通过 FeatureMap(0xFFFC)声明设备支持的可选能力:

Bit 0
WTRMRK(Watermarks) 高水位线追踪 —— 支持 CurrentHeapHighWatermark 属性和 ResetWatermarks 命令,记录堆内存使用的历史峰值
Feature 含义

FeatureMap = 0x01(WTRMRK):设备追踪堆内存使用峰值,可通过 ResetWatermarks 命令重置。
FeatureMap = 0x00(无 Feature):仅提供实时堆内存和线程指标,不记录历史峰值。

属性

所有属性均为只读。堆内存相关属性为可选,ThreadMetrics 也为可选。点击属性 ID 可跳转到详细说明。

ID 名称 类型 条件 说明
0x00 ThreadMetrics list<ThreadMetricsStruct> 可选 当前运行的线程列表及栈使用情况
0x01 CurrentHeapFree uint64 可选 当前堆空闲字节数
0x02 CurrentHeapUsed uint64 可选 当前堆已用字节数
0x03 CurrentHeapHighWatermark uint64 WTRMRK 堆使用历史峰值(高水位线)

ThreadMetrics(线程指标列表)

返回设备当前所有运行线程的栈使用信息。每个条目是一个 ThreadMetricsStruct,包含线程 ID、名称和栈使用统计。 这是排查栈溢出的关键数据来源。

ThreadMetricsStruct 结构

ID 字段 类型 必填 说明
0x00 Id uint64 是 线程唯一标识符
0x01 Name string(最长 8 字符) 可选 线程名称(如 "Main"、"BLE"、"WiFi")
0x02 StackFreeCurrent uint32 可选 当前栈剩余空闲字节数
0x03 StackFreeMinimum uint32 可选 栈剩余空闲的历史最小值(栈使用水位线)
0x04 StackSize uint32 可选 线程栈总大小(字节)
栈溢出判断

当 StackFreeMinimum 接近 0 时,意味着该线程曾经几乎用完了栈空间,存在栈溢出风险。 一般建议保持 StackFreeMinimum / StackSize > 10% 的安全余量。 如果低于这个阈值,应考虑增大该线程的栈分配或优化其调用深度。

CurrentHeapFree(堆空闲字节数)

设备堆内存中当前可用于分配的字节数。在资源受限的嵌入式设备上(如 ESP32 系列), 这个值持续下降可能意味着内存泄漏。

CurrentHeapUsed(堆已用字节数)

设备堆内存中当前已被分配使用的字节数。与 CurrentHeapFree 互补 —— 两者之和近似等于堆总大小(可能有碎片和管理开销的差异)。

CurrentHeapHighWatermark(堆使用高水位线)

自上次 ResetWatermarks 命令或设备启动以来,CurrentHeapUsed 达到过的最大值。 这个属性需要 WTRMRK Feature 支持。

高水位线是评估设备内存裕度的重要指标 —— 它反映的是「最坏情况下用了多少内存」, 而不是某一时刻的快照。即使当前 CurrentHeapUsed 看起来正常,高水位线也可能揭示间歇性的内存尖峰。

命令(Commands)

SoftwareDiagnostics 只有一个命令,需要 WTRMRK Feature 支持。

ID 名称 条件 说明
0x00 ResetWatermarks WTRMRK 重置堆使用高水位线和线程栈最小空闲值

ResetWatermarks —— 重置水位线(0x00)

将 CurrentHeapHighWatermark 重置为当前的 CurrentHeapUsed 值, 同时将所有线程的 StackFreeMinimum 重置为当前的 StackFreeCurrent 值。 该命令不接受任何参数。

使用时机

典型用法:OTA 升级后发一次 ResetWatermarks,然后观察新固件运行一段时间后的高水位线, 评估新版本的内存占用是否有回退。也适用于排查特定操作的内存影响 —— 重置后执行操作,再读取水位线。

请求示例:

{
  "invokeRequests": [{
    "commandPath": {
      "endpointId": 0,
      "clusterId": "0x0034",
      "commandId": "0x00"       // ResetWatermarks
    },
    "commandFields": {}         // 无参数
  }]
}

事件(Events)

设备运行过程中检测到软件故障时,会上报 SoftwareFault 事件。该事件为可选支持。

ID 名称 优先级 说明
0x00 SoftwareFault Info 检测到软件故障时触发

SoftwareFault —— 软件故障事件(0x00)

当设备固件检测到软件异常(如未处理的异常、断言失败、看门狗触发等)时上报此事件。 事件数据携带故障线程信息和可选的故障现场记录。

字段ID类型说明
Id 0x00 uint64 故障发生时的线程 ID
Name 0x01 string(最长 8 字符) 故障线程名称(可选)
FaultRecording 0x02 octstr(最长 1024 字节) 故障现场数据,格式由厂商定义(可选)
FaultRecording 的用途

FaultRecording 是一段厂商自定义的二进制数据,可能包含寄存器快照、调用栈回溯、 崩溃地址等调试信息。不同芯片平台的格式不同,需要配合厂商的解码工具使用。 App 端通常只需要将原始数据上传到云端,由后台服务解析。

事件上报示例:

{
  "eventReports": [{
    "eventData": {
      "path": {
        "endpointId": 0,
        "clusterId": "0x0034",
        "eventId": "0x00"       // SoftwareFault
      },
      "eventNumber": 7,
      "priority": "INFO",
      "data": {
        "0": 42,                // Id = 42(故障线程 ID)
        "1": "BLE",             // Name = "BLE"(故障线程名)
        "2": "RkVUQ0g6IDB4..."  // FaultRecording(Base64 编码的故障现场数据)
      }
    }
  }]
}

示例数据

读取一个 ESP32 设备的 SoftwareDiagnostics Cluster 属性:

{
  // --- 属性 ---
  "0x0": [                       // ThreadMetrics(线程指标列表)
    {
      "0": 1,                    // Id = 1
      "1": "Main",               // Name = "Main"
      "2": 2048,                 // StackFreeCurrent = 2048 字节
      "3": 1024,                 // StackFreeMinimum = 1024 字节
      "4": 8192                  // StackSize = 8192 字节
    },
    {
      "0": 2,                    // Id = 2
      "1": "BLE",                // Name = "BLE"
      "2": 4096,                 // StackFreeCurrent = 4096 字节
      "3": 2048,                 // StackFreeMinimum = 2048 字节
      "4": 8192                  // StackSize = 8192 字节
    }
  ],
  "0x1": 65536,                  // CurrentHeapFree = 64 KB
  "0x2": 131072,                 // CurrentHeapUsed = 128 KB
  "0x3": 196608                  // CurrentHeapHighWatermark = 192 KB(需 WTRMRK Feature)
}

常见场景

场景 1:内存泄漏监控

设备长时间运行后响应变慢、功能异常,怀疑存在内存泄漏。

  1. 定期读取 CurrentHeapFree 和 CurrentHeapUsed(如每小时一次)
  2. 记录到时序数据库或日志中,绘制内存趋势图
  3. 如果 CurrentHeapFree 持续下降且不回升,基本可以确认存在内存泄漏
  4. 结合 ThreadMetrics 排查是否某个线程的栈使用异常
  5. 进一步通过 OTA 修复后,重置水位线观察新版本表现
场景 2:线程栈健康检查

设备偶发崩溃重启,怀疑某个线程栈空间不足导致溢出。

  1. 读取 ThreadMetrics 列表,关注每个线程的 StackFreeMinimum
  2. 计算栈使用率:(StackSize - StackFreeMinimum) / StackSize
  3. 使用率超过 90% 的线程有栈溢出风险,需要关注
  4. 对比 StackFreeCurrent 和 StackFreeMinimum 的差距 —— 差距越大说明栈使用波动越剧烈
  5. 调整固件中对应线程的栈分配大小,OTA 后再次检查
场景 3:OTA 后高水位线对比

固件升级后需要评估新版本的内存表现是否有回退。需要 WTRMRK Feature 支持。

  1. OTA 完成后,设备重启,水位线自动重置
  2. 让设备正常运行一段时间(建议 24-48 小时,覆盖各种使用场景)
  3. 读取 CurrentHeapHighWatermark,与旧版本的记录对比
  4. 如果新版本水位线明显高于旧版本,说明新代码引入了额外的内存开销
  5. 也可以手动发 ResetWatermarks 后执行特定操作,精确测量该操作的内存峰值