HiSpark编程指南

- 调测资料
  - SDK Sample使用指南
    - 前言
    - 概述
    - Sample框架
    - 外设 Sample
    - SLE UART Sample
    - SLE UART DMA Sample
    - SLE Mouse Sample
    - SLE amic vdt with dongle
    - SLE microphone
    - SLE measure dis
    - BLE Lowpower Sample
    - BLE UART Sample
    - BLE Keyboard Sample
    - BLE SLE TAG Sample
    - BLE Mouse Sample
    - USB Keyboard Sample
    - USB Mouse Sample
    - USB AMIC VDT Sample
    - RCU
    - 多连接
    - air mouse
    - SLE MOUSE with Dongle
    - SLE OTA dongle
    - game mouse
    - BLE UUID
    - 缩略语
  - SDK二次开发网络安全注意事项
    - 前言
    - 产品安全解决方案
    - 结论
  - SDK开发环境搭建用户指南
    - 前言
    - 开发环境搭建
    - 编译SDK
    - 新建APP
  - 内存维测指导书
    - 前言
    - 简介
    - 内存维测功能
  - 射频AT测试指南
    - 前言
    - 射频测试AT指令
    - AT指令组包示例
    - 注意事项
  - 维测指导书
    - 前言
    - 概述
    - 日志说明
    - 日志分析
    - 问题定位
    - 典型案例
    - 常见问题FAQ
- 软件资料
  - AT命令使用指南
    - 前言
    - 指令说明
    - 通用AT指令
    - BT业务AT指令
    - 新增AT指令方法
  - DFU升级APK开发指南
    - 前言
    - 功能特性
    - 系统框架
    - 工程文件部署
    - 界面实现
    - 升级界面Fragment实现
    - 关于界面Fragment实现
    - 应用清单AndroidManifest
  - DFX软件开发指南
    - 前言
    - DIAG维测功能
    - 死机诊断
    - Dump解析
  - EFUSE使用指南
    - 前言
    - 概述
    - 使用软件接口读写EFUSE
    - 使用BurnTool烧写EFUSE
  - NFC TAG软件开发指导书
    - 前言
    - 概述
    - 开发指导
  - NV存储用户指南
    - 前言
    - NV简介
    - NV编译预置
    - NV API指南
    - 定制化NV结构使用介绍
    - 通过NV自定义传输数据最大长度
  - Release notes
    - 1.0.18版本描述文件
    - 1.0.17版本描述文件
    - 1.0.16版本描述文件
  - SDK开发指南
    - 前言
    - 概述
    - 系统接口
  - SDK接口变更说明书
    - 前言
    - BLE SDK接口变更说明
  - SP31 SLP使用指南
    - 开源版本
  - USB HID复合设备开发指南
    - 前言
    - 概述
    - 实现方式
  - 代码框架说明
    - 前言
    - SDK模块框架
    - SDK目录结构
    - SDK目录说明
  - 低功耗FAQ
    - 前言
    - 系统无法睡眠
    - IO无法唤醒系统
    - 唤醒后外设异常
    - 低功耗导致OTA升级异常
    - 功耗比预期高
    - 锁中断导致BT业务阻塞
    - 概率性狗超时
    - 供电模式&软件配置
    - 提前唤醒时间问题
  - 低功耗sample使用指南
    - 前言
    - 编译&测试
    - 系统状态管理
    - 用户指南
  - 低功耗开发指南
    - 前言
    - 低功耗概述
    - 低功耗方案
    - 开发指引
    - 功耗优化
    - 常见问题
  - 功率配置说明书
    - 前言
    - 射频功率配置说明
  - 升级方案使用指南
    - 前言
    - 概述
    - 升级包制作
    - 升级包传输
    - 升级包保存
    - 升级包本地升级
    - FAQ
  - 快速入门指南
    - 前言
    - 文档目录
    - 环境搭建
    - 基础功能开发
    - 调优
    - 特性开发
    - 测试报告
  - 星闪应用层SLE-Link协议
    - 前言
    - 概述
    - SLE Frame格式定义
    - 服务接口定义
    - 交互流程示例
  - 星闪软件开发指导书
    - 前言
    - 概述
    - Device Discovery接口
    - Connection Manager接口
    - SSAP Server接口
    - SSAP client接口
    - Low Latency接口
    - Transmission Manager接口
    - SLE Passkey接口
    - SLE Channel Map 接口
  - 红外学习使用指南
    - 前言
    - 红外学习方式
    - 通过比较器进行红外学习
    - 使用指南
  - 蓝牙软件开发指导书
    - 前言
    - 概述
    - GAP接口
    - GATT server接口
    - GATT client接口
  - 设备驱动开发指南
    - 前言
    - Pinctrl
    - GPIO
    - UART
    - SPI
    - I2C
    - ADC
    - DMA
    - PWM
    - WDT
    - Timer
    - Systick
    - TCXO
    - Flash
    - SFC
    - QDEC
    - KEYSCAN
    - PDM
    - USB_HID
    - USB_DFU
    - I2S
    - TRNG
    - 附：MIDDLEWARE
  - 音频编解码开发指导书
    - 前言
    - 概述
    - Audio Encoder
    - Audio Decoder
    - 调试与功能
- 工具资料
  - BurnTool工具使用指导书
    - 前言
    - BurnTool工具简介
    - 操作指南
    - FAQ
  - HiSpark Studio使用指南
    - 前言
    - 工具简介
    - 开发环境搭建
    - 工程管理
    - 工程配置
    - 编译运行
    - 软件包烧录
    - 栈分析和镜像分析
    - 工程调试
    - 串口控制台工具
    - Kconfig配置
    - GUI工程创建与使用
    - 高阶分析功能使用
    - 常见错误

### 该页内容

- 前言
- DIAG维测功能
  - 概述
  - 功能描述
  - 日志打印功能
    - 场景说明
    - 工作流程
    - 代码示例
  - 命令注册功能
    - 场景说明
    - 工作流程
    - Database修改
  - 系统维测接口
    - 内存使用统计
    - 任务信息统计
- 死机诊断
  - 概述
  - 死机信息获取
    - 串口界面输出
    - DebugKits工具获取信息
    - 连接J-Link调试器导出
  - 死机问题定位
    - 串口死机信息查看
      - 异常信息汇总
      - CPU寄存器信息
      - 函数调用栈信息
    - last word信息查看
    - 内存last dump信息查看
    - 看门狗重启问题定位
    - CPU异常触发重启问题定位
      - Store或Load异常
      - 取指异常
- Dump解析
  - 概述
  - 功能描述
    - 场景说明
    - 工作流程
    - 异常处理

# 前言

概述

本文档主要介绍BS2X DFX功能及使用指南，包括DIAG维测功能、死机诊断、Dump解析等，方便用户进行业务维测及死机分析。

产品版本

与本文档相对应的产品版本如下。

产品名称 | 产品版本
--- | ---
BS2X | V100

读者对象

本文档主要适用于以下工程师：

- 软件工程师

- 技术支持工程师

符号约定

在本文中可能出现下列标志，它们所代表的含义如下。

符号 | 说明
--- | ---
| 表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。
| 表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。
| 表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。
| 用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。
| 对正文中重点信息的补充说明。 “说明”不是安全警示信息，不涉及人身、设备及环境伤害信息。

修改记录

文档版本 | 发布日期 | 修改说明
--- | --- | ---
04 | 2025-08-07 | 更新“代码示例”小节内容。
03 | 2025-03-26 | 更新“系统维测接口”小节内容。
02 | 2024-07-04 | 更新“日志打印功能”的“工作流程”小节内容。 更新“DebugKits工具获取信息”小节内容。
01 | 2024-05-15 | 第一次正式版本发布。
00B01 | 2024-04-10 | 第一次临时版本发布。

# DIAG维测功能

## 概述

DIAG维测功能是基于单板与DebugKits工具的交互，来支持单板诊断业务和其他维测相关业务开发。

## 功能描述

DIAG维测功能模块提供以下功能：

- 日志打印。用户可以通过日志打印接口将调试信息打印到DebugKits的Message界面。

- 命令的注册和处理。用户可以通过注册命令和命令处理函数，来实现在DebugKits工具的命令行界面输入命令来控制单板侧的操作。

- 系统维测信息获取。DIAG支持获取系统统计类信息（如内存使用、任务信息），帮助用户定位问题。

## 日志打印功能

### 场景说明

用户需要增加一些调试日志来定位问题时，可以通过DIAG提供的日志打印接口，将调试信息打印到DebugKits的Message界面。

### 工作流程

以用户在drivers/chips/bs2x/app_os_init.c中增加调试日志为例，流程如下：

1. 在drivers/chips/bs2x/app_os_init.c中调用日志打印接口，输出调试信息。需要包含diag_log头文件。

   表 1 日志打印接口

   函数 | 说明
   --- | ---
   uapi_diag_error_log | 输出ERROR级别的调试日志（可变参数），最多带10个参数。
   uapi_diag_warning_log | 输出WARNING级别的调试日志（可变参数），最多带10个参数。
   uapi_diag_info_log | 输出INFO级别的调试日志（可变参数），最多带10个参数。
   uapi_diag_debug_log | 输出DEBUG级别的调试日志（可变参数），最多带10个参数。

2. 用户在使用DIAG日志之前，需要先确认使用的模块。在drivers/chips/bs2x/main_init.c文件中默认使用的是LOG_PFMODULE模块，模块ID的定义详见middleware/utils/dfx/log/include/log_module_id.h文件。

   如果用户在自定义的目录，自己的代码中使用日志，需要在该目录的CMakeLists.txt文件中增加如下命令：

       set(MODULE_NAME "app")
       set(AUTO_DEF_FILE_ID TRUE)

   其中：“app”为模块名称，表示用户在这个组件中使用app模块（模块ID为LOG_APPMODULE）的日志。

3. 定义该文件的文件ID，并添加到对应模块的文件ID列表文件中。

   如：

   - PF模块的ID列表文件是：middleware/chips/bx2x/dfx/include/log_def_pf.h

   - APP模块的ID列表文件是：middleware/chips/bx2x/dfx/include/log_def_app.h

   其他模块依次类推。

   文件ID的格式为：调用日志接口的文件的文件名大写并加_C后缀，如：MAIN_C、APP_OS_INIT_C。

4. 编译程序，编译过程中将生成output/bx2x/database_evb目录。

5. 在DebugKits中执行update HDB，将上一步骤生成的数据库更新至DebugKits的数据库中。打开DebugKits的Message界面查看日志信息（详见《BS2XV100 DebugKits工具 使用指南》）。

### 代码示例

    #include "diag_log.h"
    int main()
    {
        uapi_diag_info_log(0, "test out info log. value = %d", 1);
        uapi_diag_error_log(0, "test out error log. a = %d, b = %d\r\n", 3, 4);
        uapi_diag_warning_log(0, "test out warning log. a = %d, b = %d c= 0x%x\r\n", 3, 4, 5);
    }

> 说明： 请注意，DIAG日志只能支持长度32位及以下的参数，如：“%d”、“%u”、“%x”、“%p”，无法支持长度大于32位的参数，如“%ld”、“%s”，无法支持浮点参数，如“%f”。

## 命令注册功能

### 场景说明

用户可以通过注册命令和命令处理函数，来实现在DebugKits工具的命令行界面输入命令来控制单板侧的操作。

### 工作流程

要想实现通过DIAG命令来控制单板的操作，需要DIAG（单板侧）和DebugKits（工具侧）约定命令和应答的ID，以及命令和应答所发送的数据的数据结构。这些约定需通过预先配置的XML文件来定义。

以增加一条“get_user_info”命令为例，流程如下：

1. 在middleware/chips/bx2x/dfx/include/soc_diag_cmd_id.h文件中定义命令ID，注意不要与已存在的命令ID重复。

   代码示例如下：

       #define DIAG_CMD_GET_USER_INFO        0x8011

2. 调用uapi_zdiag_register_cmd函数，注册该命令的回调函数。

   其中第一个参数“cmd_tbl”表示注册的命令表格，cmd_num表示命令表格中命令个数。可参考middleware/chips/bx2x/dfx/dfx_system_init.c文件中register_default_diag_cmd函数的实现。

   代码示例如下：

       zdiag_cmd_reg_obj_t g_diag_user_cmd_tbl[] = {
           { DIAG_CMD_GET_USER_INFO, DIAG_CMD_GET_USER_INFO, diag_cmd_get_user_info},
       };

       static errcode_t register_diag_user_cmd(void)
       {
           return uapi_zdiag_register_cmd(g_diag_user_cmd_tbl,
           sizeof(g_diag_user_cmd_tbl) / sizeof(g_diag_user_cmd_tbl[0]));
       }

3. 在2中注册的回调函数diag_cmd_get_user_info中，实现收到该命令后的操作，如果需要给DebugKits工具应答，则调用uapi_zdiag_report_packet函数，上报报文。

   其中cmd_id为上报ID，option使用DIAG_OPTION_INIT_VAL宏初始化，packet为上报的数据内容指针，packet_size为上报的数据内容大小，sync表示同步或异步上报。

   代码示例如下：

       errcode_t diag_cmd_get_mem_info(uint16_t cmd_id, void * cmd_param, uint16_t cmd_param_size, diag_option_t *option)
       {
           errcode_t ret;
           mdm_user_info_t info;

           uapi_unused(cmd_param);
           uapi_unused(cmd_param_size);

           /* 获取用户的数据信息 */
           ret = dfx_mem_get_sys_user_info(&info);
           if (ret != ERRCODE_SUCC) {
               return ret;
           }

           /* 将用户的数据信息发送给DebugKits */
           uapi_zdiag_report_packet(cmd_id, option, (uint8_t *)&info, (uint16_t)sizeof(mdm_user_info_t), true);
           return ERRCODE_SUCC;
       }

4. 在build/config/target_config/bx2x/hdb_config/database_template/acore/system/hdbcfg/mss_cmd_db.xm中定义命令。代码示例如下：

       <DebugKits>
         <GROUP NAME="AUTO" DATA_STRUCT_FILE="..\diag\apps_core_hso_msg_struct_def.txt" MULTIMODE="Firefly" PLUGIN="0x111,0x110(1),0x252">
         </GROUP>
         <GROUP NAME="FIX" DATA_STRUCT_FILE="..\diag\fix_struct_def.txt" MULTIMODE="Firefly" AUTO_STRUCT="YES" PLUGIN="0x111,0x110(1),0x252">
           <CMD ID="0x5071" NAME="get_user_info" DESCRIPTION="get_mem_info" PLUGIN="0x100,0x102" TYPE="REQ_IND">
             <REQ STRUCTURE="tool_null_stru" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mdm_user_info_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x71C0" NAME="diag_dfx" DESCRIPTION="diag_test_cmd" PLUGIN="0x100,0x252" TYPE="REQ_IND">
             <REQ STRUCTURE="diag_dfx_cmd_req_st" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="diag_dfx_cmd_ind_st" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x71C1" NAME="ind_diag_dfx_stat" DESCRIPTION="diag_test_cmd" PLUGIN="0x100,0x252" TYPE="IND">
             <IND STRUCTURE="zdiag_dfx_stat" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x5073" NAME="get_task_info" DESCRIPTION="get_task_info" PLUGIN="0x100,0x102" TYPE="REQ_IND">
             <REQ STRUCTURE="tool_null_stru" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="ext_task_info" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x5074" NAME="mem32" DESCRIPTION="mem32" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_read_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_read32_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x5075" NAME="mem16" DESCRIPTION="mem16" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_read_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_read16_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
            <CMD ID="0x5076" NAME="mem8" DESCRIPTION="mem8" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_read_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_read8_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
            <CMD ID="0x5077" NAME="w1" DESCRIPTION="w1" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_write_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_write_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
            <CMD ID="0x5078" NAME="w2" DESCRIPTION="w2" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_write_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_write_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
            <CMD ID="0x5079" NAME="w4" DESCRIPTION="w4" PLUGIN="0x100,0x102,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="mem_write_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="mem_write_ind_t" TYPE="Auto" RESULT_CODE="" />
           </CMD>
           <CMD ID="0x7194" NAME="tranmit_reply" DESCRIPTION="" PLUGIN="0x100,0x259,0x261" TYPE="IND">
             <IND STRUCTURE="transmit_data_reply_pkt" TYPE="Auto" />
           </CMD>
           <CMD ID="0x71D2" NAME="sample_data" DESCRIPTION="" PLUGIN="0x100,0x259" TYPE="REQ_IND">
             <REQ STRUCTURE="diag_sample_data_cmd_t" TYPE="Auto" PARAM_VALUE="" />
             <IND STRUCTURE="diag_sample_data_ind_t" TYPE="Auto" />
           </CMD>
           <CMD ID="0x71A4" NAME="last_dump" DESCRIPTION="" PLUGIN="0x100,0x110,0x261" TYPE="IND">
             <IND STRUCTURE="last_dump_data_ind_t" TYPE="Auto" />
           </CMD>
           <CMD ID="0x71A5" NAME="last_dump_finish" DESCRIPTION="" PLUGIN="0x100,0x110,0x261" TYPE="IND">
             <IND STRUCTURE="last_dump_data_ind_finish_t" TYPE="Auto" />
           </CMD>
           <CMD ID="0x71A6" NAME="last_word" DESCRIPTION="" PLUGIN="0x100,0x110" TYPE="IND">
             <IND STRUCTURE="diag_last_word_ind_t" TYPE="Auto" />
           </CMD>
         </GROUP>
       </DebugKits>

5. 将mss_cmd_db.xml数据库文件更新至DebugKits工具数据库中，打开DebugKits命令行界面，即可输入get_user_info命令，实现获取用户数据信息的功能。

### Database修改

上述工作流程中的“4”修改的mss_cmd_db.xml文件，是DIAG与DebugKits约定数据结构的关键。下面对其详细说明：

- mss_cmd_db.xml中分为两层：GROUP和CMD，每个GROUP可以包含多个CMD命令。

- GROUP中的DATA_STRUCT_FILE字段指向的文件名，表示该GROUP下的命令涉及到的数据结构体都保存在这个文件中。

- CMD中的REQ STRUCTURE字段指向请求命令携带数据对应的结构体，IND STRUCTURE指向应答命令携带的数据对应的结构体。

- CMD中PLUGIN字段表示该命令在DebugKits生效的界面：

  - 0x100：命令行页面

  - 0x259：system页面

  - 0x110：message页面

- GROUP中的AUTO_STRUCT字段用来配置结构体是否自动生成，YES表示构建过程中自动生成database的结构体，否则需要自己手动添加到结构体到DATA_STRUCT_FILE字段指向的文件中。例如：

      /*mss_cmd_db.xml中添加一条get_user_info命令，CMD ID为0x5071*/
          <CMD ID="0x5071" NAME="get_user_info" DESCRIPTION="get_user_info" PLUGIN="0x100,0x102" TYPE="REQ_IND">
            <REQ STRUCTURE="tool_null_stru" TYPE="Auto" PARAM_VALUE="" />
            <IND STRUCTURE="mdm_user_info_t" TYPE="Auto" RESULT_CODE="" />
          </CMD>
      /*fix_struct_def.txt中添加结构体定义*/
      typedef struct {
          uint32_t user_info1;
          uint32_t user_info2;
          uint32_t user_info3;
          uint32_t user_info4;
          uint32_t user_info5;
          uint32_t user_info6;
      } mdm_user_info_t;

## 系统维测接口

系统维测功能提供了以下接口：

- 内存使用统计接口

- 任务信息统计接口

### 内存使用统计

内存使用统计功能为用户提供内存使用的信息查询接口，可实时获取内存使用大小、峰值占用等信息。

功能描述

DIAG提供内存使用统计信息查询接口diag_cmd_get_mem_info，调用该接口可获取系统内存池总大小、已用空间、剩余空间、剩余空间节点个数、内存池已经使用的节点个数、内存池剩余空间中最大节点的大小以及内存池使用峰值等信息。以上信息以mdm_mem_info_t结构体的格式，发送到DebugKits中显示出来。

    typedef struct {
        uint32_t total;                /* Total space of the memory pool (unit: byte).
                                        CNcomment:内存池总大小（单位：byte）CNend */
        uint32_t used;                 /* Used space of the memory pool (unit: byte).
                                        CNcomment:内存池已经使用大小（单位：byte）CNend */
        uint32_t free;                 /* Free space of the memory pool (unit: byte).
                                        CNcomment:内存池剩余空间（单位：byte）CNend */
        uint32_t free_node_num;        /* Number of free nodes in the memory pool.
                                        CNcomment:内存池剩余空间节点个数 CNend */
        uint32_t used_node_num;        /* Number of used nodes in the memory pool.
                                        CNcomment:内存池已经使用的节点个数 CNend */
        uint32_t max_free_node_size;   /* Maximum size of the node in the free space of the memory pool (unit: byte).
                                        CNcomment:内存池剩余空间节点中最大节点的大小（单位：byte）CNend */
        uint32_t peek_size;            /* Peak memory usage of the memory pool.CNcomment:内存池使用峰值CNend */
    } mdm_mem_info_t;

命令行代码示例

    errcode_t diag_cmd_get_mem_info(uint16_t cmd_id, void * cmd_param, uint16_t cmd_param_size, diag_option *option)
    {
        errcode_t ret;
        mdm_mem_info_t info;
        uapi_unused(cmd_param);
        uapi_unused(cmd_param_size);
        ret = dfx_mem_get_sys_pool_info(&info);
        if (ret != ERRCODE_SUCC) {
            return ret;
        }
        uapi_zdiag_report_packet(cmd_id, option, (uint8_t)&info, (uint16_t)sizeof(mdm_mem_info_t), true);
        return ERRCODE_SUCC;
    }

### 任务信息统计

任务信息统计功能可查询任务名、任务状态、当前SP、优先级、栈峰值、栈大小等信息。

功能描述

提供任务统计功能查询接口diag_cmd_get_task_info，调用该接口可实时获取当前系统任务ID、任务状态、任务优先级、信、栈峰值、栈大小等信息。上述数据以task_info_t结构体的格式，发送到DebugKits中显示出来。如图1所示。

图 1 任务信息统计示例

    typedef struct {
        char name[DFX_TASK_NAME_LEN]; /* Task entrance function.CNcomment:入口函数CNend */
        bool valid;
        uint32_t id;                      /* Task ID.CNcomment:任务ID CNend */
        uint16_t status;                  /* Task status. Status detail see los_task_pri.h.CNcomment:任务状态。
                                             详细状态码请参考los_task_pri.h CNend */
        uint16_t priority;                /* Task priority.CNcomment:任务优先级 CNend */
        void *task_sem;                   /* Semaphore pointer.CNcomment:信号量指针CNend */
        void *task_mutex;                 /* Mutex pointer.CNcomment:互斥锁指针CNend */
        uint32_t event_stru[3];           /* Event: 3 nums.CNcomment:3个事件CNend */
        uint32_t event_mask;              /* Event mask.CNcomment:事件掩码CNend */
        uint32_t stack_size;              /* Task stack size.CNcomment:栈大小CNend */
        uint32_t top_of_stack;            /* Task stack top.CNcomment:栈顶CNend */
        uint32_t bottom_of_stack;         /* Task stack bottom.CNcomment:栈底CNend */
        uint32_t sp;                      /* Task SP pointer.CNcomment:当前SP.CNend */
        uint32_t curr_used;               /* Current task stack usage.CNcomment:当前任务栈使用率CNend */
        uint32_t peak_used;               /* Task stack usage peak.CNcomment:栈使用峰值CNend */
        uint32_t overflow_flag;           /* Flag that indicates whether a task stack overflow occurs.
                                           CNcomment:栈溢出标记位CNend */
    } task_info_t;

命令行代码示例

    errcode_t diag_cmd_get_task_info(uint16_t cmd_id, void * cmd_param, uint16_t cmd_param_size, diag_option *option)
    {
        uint32_t task_cnt;
        errcode_t ret;
        task_info_t *infs = NULL;
        uapi_unused(cmd_param);
        uapi_unused(cmd_param_size);
        task_cnt = dfx_os_get_task_cnt();
        if (task_cnt == 0) {
            return ERRCODE_FAIL;
        }
        infs = dfx_malloc(0, task_cnt * sizeof(ext_task_info));
        if (infs == TD_NULL) {
            return ERRCODE_FAIL;
        }
        ret = dfx_os_get_all_task_info(infs, task_cnt);
        if (ret != ERRCODE_SUCC) {
            dfx_free(0, infs);
            return ret;
        }
        for (unsigned i = 0; i < task_cnt; i++) {
            task_info_t *inf = &infs[i];
            if (inf->valid) {
                uapi_zdiag_report_packet(cmd_id, option, (uint8_t *)inf, (uint16_t)sizeof(task_info_t), true);
            }
        }
        dfx_free(0, infs);
        return ERRCODE_SUCC;
    }

# 死机诊断

## 概述

BS2X异常复位包含系统异常复位和Panic主动复位。

异常复位记录系统异常信息，后统称死机信息。包括死机现场的PC地址、返回地址、栈地址、CPU寄存器信息以及部分内存信息等。在主动复位场景下，还会记录用户导入的维测信息。死机信息可以通过串口打印、链接J-link工具、链接DebugKits工具进行获取，用户可根据实际使用场景进行获取和分析。

> 说明： 通过串口捕获死机信息通常运用在日常代码开发调试、业务挂测等开发测试场景，一些串口没有打印的内存信息可以通过J-link工具链接获取。在无调试串口的场景下，建议通过DebugKits工具提供的死机信息进行分析定位。

## 死机信息获取

死机信息可通过以下方法获取：

- 通过串口工具获取死机打印信息。

- 通过DebugKits工具获取死机信息。

- 连接J-Link调试器获取死机信息。

各种工具的具体连接方式会在下面小章节进行描述。在研发测试实验室调测场景下，上述3种方法均适用。在外网场景下适用通过DebugKits工具来获取死机信息。

> 说明： 死机信息通过串口打印，要保证死机时单板已连接串口工具。

### 串口界面输出

硬件链接之后，打开串口工具界面。选择对应的端口号之后点击打开串口进行连接，波特率选择115200，如图1所示。

图 1 串口连接

当硬件与串口工具处于连接状态时，如果单板发生死机，串口工具能够接收到复位前OS打印的死机信息。如图2所示。

图 2 串口工具中捕获的死机信息

### DebugKits工具获取信息

工具准备

首先进入工具页面点击options再选择change chip，在弹框中选择BS2X确定芯片类型，参考图1。

图 1 选择芯片

图 2 修改芯片类型为BS21

然后点击Connection选择Connect在弹窗中选择连接方式以及对应的通道序号进行连接，参考图3。

图 3 连接芯片

如果是第一次连接芯片或者芯片有新版本的程序，需要更新HDB并重新连接之后，日志的打印才能正确的显示，在Option菜单下选择Update HDB，选择生成的database目录（\output\bx2x\database_evb）再点击OK即可完成配置，具体参考图4。

图 4 配置database

通过DebugKits工具可以实时观测芯片运行时打印的具体信息，可以参考图5。当死机发生时Message视图将会打印出last word以及last dump信息。

图 5 DebugKits日志打印

获取last word

last word信息是死机发生时，将PC地址、返回地址、栈地址、CPU寄存器信息发送出来，用户可以根据last word的具体信息分析错误类型、定位错误原因，具体可以参考图6。

图 6 last word信息

获取last dump信息

last dump信息将会把内存中的部分信息打包生成bin文件存储到DebugKits的安装目录中的DumpInfo子目录之下，具体参考图7。用户可以通过解析这些文件定位错误位置。使用方法详见Dump解析。

图 7 last dump生成文件

> 说明： 将内存dump到DebugKits中需要一定时间（Liteos：1～2 min，FreeRtos：4～5 min），因此如果需要使用last dump分析死机信息，注意不要在发生死机故障后立即复位，否则last dump信息可能无法完整获取。

### 连接J-Link调试器导出

在允许连接JTAG调试器的场景下，用户可连接劳特巴赫或J-Link仿真器等工具进一步获取死机现场的相关信息。以下主要介绍通过J-Link仿真器获取死机相关信息的过程。

在条件允许的情况下，建议开启死机不复位功能进行死机定位。死机现场时，连接J-Link，可以查看“串口界面输出”小节所展示的信息，还可查询内存信息、栈信息、外设寄存器、维测变量等更丰富的数据。本小节主要介绍在死机现场使用J-Link调试器导出死机的过程。

使用J-Link仿真器调试需先连接单板的20PIN排针，具体排针位置以实际产品为准。

硬件连接后，双击工具包（sdk\tools\bin\jlink_tool\bx2x）中的Commandline_bx2x_mcpu.bat脚本，脚本将自动进行连接（其他核的连接操作可参照此方法进行）。

图 1 J-Link连接MCPU示意图

> 须知： 注意RISC-V架构处理器需要使用V10及以上版本的J-Link仿真器。

表 1 J-Link常用命令

命令 | 描述
--- | ---
con/connect | 连接。
h/halt | 暂停，停止。
g/go | 继续，运行。
mem32 mem16 mem8 | I/O读32bit：mem32 <Addr>, <NumItems> (hex) (Addr表示内存地址 NumItems表示从Addr开始连续读几个32bit ) I/O读16bit：mem16 <Addr>, <NumItems> (hex) I/O读8bit：mem8 <Addr>, <NumItems> (hex)
w4 w2 w1 | I/O写4Byte：w4 <Addr>, <Data> (hex) I/O写2Byte：w2 <Addr>, <Data> (hex) I/O写1Byte：w1 <Addr>, <Data> (hex)
readcsr | 读riscv csr寄存器：ReadCSR <RegIndex>
writecsr | 写riscv csr寄存器：WriteCSR <RegIndex>,<Value>

查询I/O信息

1. 查询CPU寄存器信息，如图2所示。

   图 2 查询CPU寄存器值

2. 查询内存信息，获取维测变量值或普通变量值。

   1. 在\output\bx2x\xxx_core\xxx_bx2x_xxx\xxx.nm中获取nm文件，在nm文件中获取想要查询的变量地址如图3中“g-exception-dump-callback”变量的地址为0x20025950。

      图 3 变量地址

   2. 通过J-Link获取状态信息，如图4。

      图 4 变量值

> 说明： 以上地址信息仅作为演示使用，查询步骤供用户参考使用。

## 死机问题定位

### 串口死机信息查看

在死机故障发生时，一般会在串口输出串口界面输出中描述的死机信息。其中包含几个部分：异常信息汇总、CPU寄存器信息、函数调用栈信息。

#### 异常信息汇总

表 1 异常信息汇总

成员 | 描述
--- | ---
task | 死机的任务名称。
thrdPid | 死机的任务ID。
type | 死机类型。

#### CPU寄存器信息

表 1 死机相关CPU寄存器描述

成员 | 描述
--- | ---
mepc | 机器异常程序计数器。当发生异常时，mepc指向导致异常的指令；对于中断，mepc指向中断处理后应该恢复的位置。
mstatus | 机器状态寄存器。
mtval | 机器陷入寄存器。保存地址异常中出错的地址或者发生指令异常的指令本身，对于其他错误，其值为零。
mcause | 机器异常寄存器。保存目前异常或者中断的原因，通过查询表2得到目前异常或者中断的类型。
ccause | 与mcause类似，ccause为mcause的补充说明，对于某些异常通过读取ccause寄存器的内容可以进一步明确异常类型。

表 2 mcause&ccause异常描述表

异常码 | mcause异常描述 | ccause异常描述
--- | --- | ---
0x0 | Instruction address misaligned | Not available
0x1 | Instruction access fault | Memory map region access fault
0x2 | Illegal instruction | AXIM error response
0x3 | Breakpoint | AHBM error response
0x4 | Load address misaligned | Crossing PMP entries
0x5 | Load access fault | System register access fault
0x6 | Store/AMO address misaligned | No PMP entry matched
0x7 | Store/AMO access fault | PMP access fault
0x8 | Environment call from U-mode | CMO access fault
0x9 | Environment call from S-mode | CSR access fault
0xa | Reserved | LDM/STMIA instruction
0xb | Environment call from M-mode | ITCM write access fault
0xc | Instruction page fault | Not available
0xd | Load page fault | Not available
0xe | Reserved | Not available
0xf | Store/AMO page fault | Not available
＞0xf | Reserved | Not available

#### 函数调用栈信息

函数调用栈call stack会显示与异常相关的所有函数调用指令。用户可以根据函数调用栈检查异常发生时函数调用的上下文定位。其中call back 0为栈顶函数，其对应的ra为栈顶函数的地址，以此类推。

用户可以到output/bx2x/acore/standard-bx2x-app-evb/application.lst中找到对应的函数。

### last word信息查看

如“DebugKits工具获取信息”中描述，在发生死机故障时，同时会将last word信息发送到DebugKits工具上。

last word上报的内容及含义如表1所示（具体含义中括号里面的为代称）。

表 1 last word内容

变量名称 | 具体含义
--- | ---
stack_limit | 系统栈大小。
fault_type | 错误类型。
fault_address | 错误地址，属性值无意义。
fault_reason | 错误原因，属性值无意义。
reg_value | 保存寄存器值。
psp_value | 栈指针。
lr_value | 返回地址。
pc_value | 发生错误的指令地址（mepc）。
psps_value | 全局指针。
primask_value | 异常状态寄存器（mstatus）。
fault_mask_value | CPU访问异常地址或异常值（mtval）。
bserpri_value | 自定义异常状态寄存器（ccause）。
control_value | CPU访问异常地址或异常值（mtval）。

对于last word上面内容里面保存的寄存器的值（reg_value）具体含义如表2所示。

表 2 reg_value成员含义

成员名称 | 成员含义
--- | ---
reg_value[0] | 无意义
reg_value[1] | 返回地址（ra）
reg_value[2] | 栈指针（sp）
reg_value[3] | 全局指针（gp）
reg_value[4] | 线程指针（tp）
reg_value[5] | 临时寄存器（t0）
reg_value[6] | 临时寄存器（t1）
reg_value[7] | 临时寄存器（t2）
reg_value[8] | 被调函数需要保存用的寄存器/调用栈的帧指针（s0）
reg_value[9] | 被调函数需要保存用的寄存器（s1）
reg_value[10] | 函数参数/返回值（a0）
reg_value[11] | 函数参数/返回值（a1）
reg_value[12] | 函数参数（a2）
reg_value[13] | 函数参数（a3）
reg_value[14] | 函数参数（a4）
reg_value[15] | 函数参数（a5）
reg_value[16] | 函数参数（a6）
reg_value[17] | 函数参数（a7）
reg_value[18] | 被调函数需要保存用的寄存器（s2）
reg_value[19] | 被调函数需要保存用的寄存器（s3）
reg_value[20] | 被调函数需要保存用的寄存器（s4）
reg_value[21] | 被调函数需要保存用的寄存器（s5）
reg_value[22] | 被调函数需要保存用的寄存器（s6）
reg_value[23] | 被调函数需要保存用的寄存器（s7）
reg_value[24] | 被调函数需要保存用的寄存器（s8）
reg_value[25] | 被调函数需要保存用的寄存器（s9）
reg_value[26] | 被调函数需要保存用的寄存器（s10）
reg_value[27] | 被调函数需要保存用的寄存器（s11）
reg_value[28] | 临时寄存器（t3）
reg_value[29] | 临时寄存器（t4）
reg_value[30] | 临时寄存器（t5）
reg_value[31] | 临时寄存器（t6）

### 内存last dump信息查看

如“DebugKits工具获取信息”中描述，当死机故障发生时，同时会将当前的内存dump到DebugKits保存下来，并可使用相关工具做进一步解析。详细使用方法请参见“Dump解析”。

### 看门狗重启问题定位

看门狗死机导致的重启问题，大概率为代码中存在死循环（包含次数很大的循环）或某个业务一直被执行（例如灌包），使得IDLE任务无法得到调度，规定时间内未进行喂狗造成的重启。通常可以通过表1中的信息并结合lst文件，确定具体的死机位置。

表 1 看门狗死机关键信息

成员 | 描述
--- | ---
mepc | 通过mepc确定看门狗到期时的pc。根据概率推测可知，该代码基本为死循环中会调用的代码。
返回地址（ra）和栈 | 如果mepc在通用函数中，例如memcpy函数中，可以通过ra和栈内容进一步确认函数调用流程。

### CPU异常触发重启问题定位

通过fault_type值的含义的描述内容，可确认CPU异常触发重启具体为哪种死机问题。

表 1 CPU异常的fault_type含义

fault_type错误 | 描述
--- | ---
Instruction address misaligned | 取址地址不对齐，riscv要求指令双字节对齐位置。
Instruction access fault | 取址地址异常。
Illegal instruction | 非法指令。
Load address misaligned | 取数据的目标地址不对齐。
Load access fault | 取数据的目的地址是禁止进行读操作的地址。
Store/AMO address misaligned | 存储数据目标地址不对齐。
Store/AMO access fault | 存储数据的目的地址是禁止进行写操作的地址。

#### Store或Load异常

表 1 store或load异常关键信息

成员 | 描述
--- | ---
mepc | 通过mepc确定异常的语句。
返回地址（ra）和栈 | 如果mepc在通用函数中，如memcpy中，可以通过ra和栈内容进一步确认函数调用流程。
mtval | 通过mtval确认访问的的异常地址。
寄存器值 | 如mepc指向下面语句时，可以确认a1值是否合法。 lw a0,0(a1)：将a0存储到a1指向的位置。 sw a0,0(a1)：从a1指向位置获取内容到a0。

> 说明： 由于存储存在异步性，存储异常时mepc指向的语句可能不是异常语句。

#### 取指异常

取指异常通常为PC跑飞造成的。首先可以确认mepc的范围是否正常，通过ra和调用栈分析可以确认代码跑飞的位置。

造成代码跑飞主要原因如下：

- 存储函数跳转地址的钩子变量被踩，导致函数跳转到非法地址。

- 栈被踩，导致函数无法正确返回上一级函数而跑飞。

- 代码段本身被踩。

# Dump解析

## 概述

Dump解析功能是指死机导出文件的内容解析，将.bin文件解析为可读性较好的文本文件，为问题定位提供参考信息。

## 功能描述

Dump解析功能主要包括两个部分：

- 第一部分主要功能是解析elf文件中DWAEF信息的.debug_info段，将对应的tag解析生成python格式的class文件，同时解析符号表，获取全局变量信息。此步骤当前集成在版本构建流程中，通过build/config/target_config/bx2x/target_config.py内的gen_prase_tools宏控制，设置为True编译时会生成DebugKits解析需要的数据。

- 第二部分主要功能是将第一步生成的文件作为输入，解析死机Dump文件，生成文本信息。此步骤当前集成在DebugKits工具中。

### 场景说明

针对死机问题定位信息少，问题定位困难等场景，提供Dump解析功能，为死机问题定位提供更多的参考信息。

### 工作流程

1. 死机dump文件获取，请参考“DebugKits工具获取信息”。其中，DebugKits工具导出的last dump生成文件就是待解析的死机文件（last dump功能是否开启通过DUMP_NUM_SUPPORT、DUMP_REG_SUPPORT、SUPPORT_DFX_EXCEPTION这三个宏控制）。

2. 编译构建生成解析工具。上文已经介绍了解析功能的第一步集成在版本构建流程中，dump解析前需要先进行版本编译构建获取parse_tool解析工具。

   1. 获取源码进行版本编译，编译环境需要支持python3，dump解析功能所需脚本和中间文件在版本编译过程中生成，编译过程中会提示信息：Build parse tool success。

   2. 编译结果确认，在output目录/output/bx2x/acore/<target-name>/下生成application.nm、application.info以及parse_tool文件夹，其中parse_tool文件夹下包括如下文件。

      ├── auto_class.py 此文件在编译过程中生成，内存解析脚本需要import的python模块

      ├── auto_struct.txt database路径下mss_cmd_db.xml文件内结构体自动生成结果，可拷贝到Debugkits工具的database路径下，用于Debugkits工具解析日志。

      ├── global.txt 此文件在编译过程中生成，内存解析时需要导入的全局变量信息

      ├── config.py 内存解析脚本配置文件

      **├── **parse_basic.py 内存解析脚本

      ├── parse_elf.py 内存解析脚本

      ├── parse_freertos.py freertos系统解析脚本

      ├── parse_liteos.py liteos系统解析脚本

      ├── parse_main_phase1.py 内存解析脚本的第一阶段入口，已集成在编译构建过程中，主要目的时生成上面的auto_class.py，auto_struct.txt以及global.txt文件

      ├──parse_main_liteos206_phase2.py liteos206版本内存解析脚本的第二阶段入口，对应liteos206版本的解析，在DebugKits工具界面调用，用于将全内存.bin文件解析为用户可见的文本信息。

      ├──parse_main_liteos207_phase2.py liteos207版本内存解析脚本的第二阶段入口，对应liteos207版本的解析，在DebugKits工具界面调用，用于将全内存.bin文件解析为用户可见的文本信息。

      ├──parse_main_liteos208_phase2.py liteos208版本内存解析脚本的第二阶段入口，对应liteos208版本的解析，在DebugKits工具界面调用，用于将全内存.bin文件解析为用户可见的文本信息。

      ├── parse_main_freertos_phase2.py freertos版本内存解析脚本的第二阶段入口，对应freertos版本的解析，在DebugKits工具界面调用，用于将全内存.bin文件解析为用户可见的文本信息。

      ├── parse_print_global_var.py 内存解析脚本

      └── xml_main.py 内存解析脚本

3. 执行dump解析。在执行dump解析之前，确保运行解析工具的PC上已安装python3和DebugKits工具。

   1. 在DebugKits工具的System界面选取DumpAnalys选项，在对应的选项上选取相应的路径；如图1所示（注：也可以将编译后的parse_tool文件夹，application.nm文件及last dump内存导出文件拷贝到本地PC上进行处理，工具选取对应路径即可）。

      图 1 DebugKits工具Dump解析界面

      - Python Path是解析脚本的路径，需要用户选取，例如/output/bx2x/acore/<target-name>/parse_tool/ parse_main_****_phase2.py。根据用户使用的OS不同，Liteos版本使用parse_main_liteos208_phase2.py。

      - NM file为内存解析脚本的输入文件，在版本构建时通过编译命令解析elf文件生成，路径为/output/bx2x/acore/<target-name>/application.nm。

      - Global file为内存解析时需要导入的全局变量信息，在版本构建时通过解析脚本的第一步生成，路径为/output/bx2x/acore/<target-name>/parse_tool/global.txt。

      - File Path为待解析的内存文件路径（即通过DebugKits工具导出的内存bin文件的存储路径），只需要选取路径即可，工具会自动加载该路径下相关的.bin文件。

   2. 点击Dump Analys界面的Analys按钮，正常情况下DebugKits工具界面会显示解析结果，并且File Path路径（即工具选取的内存.bin文件路径）下会生成memory_result.txt解析结果，如果没有显示结果，则参考“异常处理”的介绍进行处理。

      图 2 Dump解析结果文件

   3. 分析解析结果。

      打开memory_result.txt文件可以查询到当前的中断信息、任务信息、消息队列、信号量、全局变量、死机信息等。

      图 3 Dump解析结果片段

### 异常处理

1. 如果执行Dump解析后DebugKits工具没有显示解析结果，首先需要确认application.nm、application.info文件、parse_tool脚本及导出的.bin文件是否出自同一个软件版本。不同版本的输入不匹配可能会导致异常。

2. DebugKits工具导入待解析的.bin文件时选取的是整个目录，要保证DebugKits工具配置文件\DebugKits\Source-Datas\bin\config\config.ini中的配置与DumpInfo文件夹下导出文件的地址及大小一致，否则也会导致解析异常。

   可以将config.ini中的DumpInfo配置项与源码middleware\chips\bx2x\dfx\last_dump_adapt.c中的g_mem_dump_info数组中的元素进行对比。如不一致，则要按照g_mem_dump_info数组的定义修改config.ini中的DumpInfo配置项。

   例如图1中的APP_ITCM_ORIGIN_StartAddr的值应该等于图2中的APP_ITCM_ORIGIN的值，APP_ITCM_ORIGIN_Length的值应该等于APP_ITCM_LENGTH的值。

   图 1 config.ini中配置信息

   图 2 g_mem_dump_info数组

3. 上述两点都排查无误后，需要手工执行脚本进行问题定位。

   方法如下：

   1. 打开DebugKits工具安装目录下的log.txt，找到工具调用解析脚本的命令。图3中cmd后面双引号内的内容即执行dump解析的命令，注意需要删除其中的转义字符“\”。

      图 3 DebugKits工具调用解析脚本的命令

   2. 将命令拷贝到cmd窗口执行，可以看到解析脚本的执行结果。图4中是正确执行的结果。如果出现错误，可以根据错误提示，进一步定位问题。

      图 4 Dump解析在命令行窗口执行结果