跳转至

DFX 软件开发指南

概述

本指南介绍 HiDiTingV100 DFX/DIAG 的日志、命令、离线记录、死机诊断、Last Dump、内存维测和 DebugKits 联调能力,以及业务组件的 DFX 接入与验证方法。

工作原理与接口

DFX / DIAG 工作原理

设备侧调用日志或诊断接口后,通过 DIAG 通道向工具侧输出消息。构建过程生成与固件匹配的数据库,DebugKits 依赖该数据库还原日志格式、命令和数据结构。离线日志、死机信息与 Last Dump 用于设备无法持续在线时的故障留存和回溯。

功能与场景选择

需求 推荐能力 文内入口
查看启动和关键状态 DIAG 日志 快速验证 DFX
在工具中触发业务查询 命令注册与报文上报 命令注册功能
工具不在线时保留日志 日志离线保存 日志离线保存功能
发生异常或复位后定位原因 死机诊断、Last Dump 死机诊断Last Dump 解析
查看内存、任务和水线 系统维测、内存 DFX 系统维测接口内存 DFX 功能
查看日志或发送命令 DebugKits DebugKits 使用说明

API 接口列表

接口的参数、返回值和约束以接口参考为准;未提供独立接口页的内容直接链接到声明头文件。

用途 接口/宏 说明
注册 DIAG 命令 uapi_diag_register_cmd 注册命令处理表
注销 DIAG 命令 uapi_diag_unregister_cmd 注销命令处理表
上报单帧数据 uapi_diag_report_packet 向工具回传数据
上报多帧数据 uapi_diag_report_packets_criticaluapi_diag_report_packets_normal 分片上报
注册主动上报 uapi_diag_register_ind 注册主动上报处理表
输出 DIAG 日志 uapi_diag_info_loguapi_diag_warning_loguapi_diag_error_loguapi_diag_debug_log 日志等级宏与参数约束
开关离线日志 uapi_zdiag_set_offline_log_enable 控制离线日志保存
注册运行回调 app_run 在应用运行阶段执行初始化函数

快速验证 DFX

功能说明

在已经参与目标构建的 Native 业务组件中增加一条 Info 日志和一条 Warning 日志,可以验证日志模块、文件 ID 配置、固件构建、数据库更新以及 DebugKits 解码链路。

工作流程

图 1 DFX工具日志验证流程

**图 1** DFX工具日志验证流程

涉及接口

准备工作

  1. 开发环境快速入门准备 SDK 编译环境,建议优先使用一站式 CLI 开发环境,并准备开发板;DebugKits 从海思开发工具下载页的“HiSpark系列工具”获取。
  2. 选择一个已经参与当前目标构建的 Native 业务组件作为承载组件;如果需要新建组件,先按OpenHarmony Native组件开发用户指南完成组件创建和构建接入。
  3. 准备与本次构建匹配的 database_evb;首次使用新固件时需要更新该数据库。

最小接入代码

在所选业务组件的源文件中包含 app_init.hdiag_log.h,使用 app_run 注册初始化函数。日志参数使用 32 位无符号整数,便于验证编码与 DebugKits 解码结果。

void dfx_log_verify_init(void)
{
    uint32_t verify_value = 1;
    uapi_diag_info_log(0, "[dfx] boot stage=%u", verify_value);
    uapi_diag_warning_log(0, "[dfx] verify value=0x%x", verify_value);
}

app_run(dfx_log_verify_init);

构建与获取固件

环境安装、目标选择、固件构建和产物获取请参见开发环境快速入门。构建完成后,以 CLI 输出的固件包和 database_evb 生成路径为准。

运行与预期结果

  1. 烧录固件并复位开发板。
  2. 在 DebugKits 中选择正确芯片和连接通道。
  3. 通过 Options > Update HDB 选择本次构建生成的 database_evb,完成后重新连接。
  4. 打开 Message 页面。

启动后应能解码出以下两条消息:

[dfx] boot stage=1
[dfx] verify value=0x1

调试方法

  • 看不到日志:确认开发板已复位、连接通道正确,并检查日志功能和模块开关。
  • 仅显示消息 ID:重新构建、更新匹配的 database_evb,然后重新连接。
  • 出现 C 未声明:确认文件 ID 已登记到所选模块的 log_def.h
  • 需要定位命令、离线日志、死机或 Last Dump 时,使用下面相应功能章节的流程。

DFX 开发与接入

本章先说明业务组件的 DFX 接入方式,再介绍日志、离线保存、命令注册、系统维测和 CHR 等 DFX 能力开发。

业务组件接入流程

  1. 在承载 DFX 代码的业务组件中加入源文件,并确认该组件已经进入当前目标的组件列表。不要为了验证日志虚构新的 Sample 宏或组件名称。
  2. 在组件的 CMakeLists.txt 中沿用所属日志模块的配置。APP 组件通常使用 MODULE_NAME "app"AUTO_DEF_FILE_ID TRUE;其他组件应保持与同目录现有实现一致。
  3. 源文件包含 /src/middleware/utils/app_init/app_init.h/src/include/middleware/utils/diag_log.h,在合适的应用阶段通过 app_run 注册初始化函数。
  4. 开启自动文件 ID 后,构建系统会按源文件名生成文件 ID。将对应枚举登记到所属模块的 log_def_*.h;APP 模块的登记位置为 /src/middleware/chips/3322/dfx/include/log_def_app.h
  5. 开发环境快速入门完成当前目标构建,并使用同一次构建生成的固件和 database_evb 进行 DebugKits 验证。

初始化函数由 app_run 放入应用运行阶段。日志宏把格式串和 32 位参数编码成 DIAG 消息;构建生成的数据库保存解码信息,因此 DebugKits 必须使用同一次构建生成的数据库。

DIAG维测功能

概述

DIAG维测功能基于单板与DebugKits工具的交互,用于单板诊断和其他维测操作。

功能描述

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

  • 日志打印。用户可以通过日志打印接口将调试信息打印到DebugKits的Message界面。
  • 离线日志保存。在没有串口的场景下,DIAG日志可以保存至NandFlash上,需要时导出至PC上进行解析。
  • 命令注册。用户可以注册自定义命令,在DebugKits工具的命令行界面输入命令与单板侧进行交互,执行用户自定义的操作。
  • 系统维测信息获取。DIAG支持获取系统统计类信息(如内存使用、任务信息),帮助用户定位问题。

日志打印功能

场景说明

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

工作流程

以用户在“/src/application/3322/3322_app_standard/main.c”中增加调试日志为例,流程如下:

  1. 在“/src/application/3322/3322_app_standard/main.c”中调用日志打印接口,输出调试信息。需要包含“diag_log.h”头文件。

    表 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日志之前,需要先确认使用的模块。在“/src/application/3322/3322_app_standard/main.c”文件中默认使用的是LOG_PFMODULE模块,模块ID的定义详见“/src/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列表文件中。

    如:

    其他模块依次类推。

    文件ID的格式:调用日志接口的文件的文件名大写并加“_C”后缀,如:MAIN_C。

  4. 编译程序,编译过程中将生成“output/3322/database_evb”目录。

  5. 参照“DebugKits使用说明”中3,将生成的数据库(database_evb)更新至DebugKits的数据库中。打开DebugKits的Message界面查看日志信息(详情请参见“DebugKits使用说明”中的4)。
  6. 如要更改默认的日志级别以及打开或关闭某些模块的日志,可修改 /src/middleware/chips/3322/dfx/diag_adapt_sdt.c 中的 diag_auto_log_report_enable 函数。如图1中的两个数组,分别是默认打开的日志级别和模块。用户可根据实际情况修改。

    图 1 打开的日志级别和模块示例

    **图 1** 打开的日志级别和模块示例

代码示例
#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"。

日志离线保存功能

场景说明

如“日志打印功能”中说明,DIAG日志可以通过串口发送至DebugKits工具。在整机或户外等没有串口的场景下,可以打开日志离线存储功能,将DIAG日志以文件的形式保存至文件系统中,需要的时候将日志文件导出到PC上,通过DebugKits工具解析。

工作流程
  1. 打印日志的方法参见“日志打印功能”。
  2. 打开/关闭离线日志存储功能特性方法:

    特性宏:dfx_feature_config.h

    示例如下:

    /* 特性: 支持日志离线存储到本地 */
    #ifndef CONFIG_DFX_SUPPORT_OFFLINE_LOG_FILE
    #define CONFIG_DFX_SUPPORT_OFFLINE_LOG_FILE         DFX_YES
    #endif
    

    在此文件中将“CONFIG_DFX_SUPPORT_OFFLINE_LOG_FILE”的值置为“DFX_YES”离线日志存储功能特性打开;置为“DFX_NO”,功能特性关闭。

  3. 打开/关闭日志离线存储功能的方法如下:

    • 方法一:在代码中调用“uapi_zdiag_set_offline_log_enable”函数。
    • 方法二:在DebugKits的命令行界面输入“offline_log_cfg”命令:

      offline_log_cfg 1:打开日志离线保存功能。

      offline_log_cfg 0:关闭日志离线保存功能。

      offline\_log\_cfg 0:关闭日志离线保存功能

    • 方法三:修改离线日志开关的NV项默认值。

      在 NV 的预置值配置文件(如 /src/middleware/chips/3322/nv/nv_config/nv_default/cfg/acore/app.json)中,将离线日志开关 NV 项 offline_enable_flag 的值改为 1。

              "offline_enable_flag":{
                  "key_id": "0x2002",
                  "key_status": "alive",
                  "structure_type": "uint8_t",
                  "attributions": 1,
                  "value": 1
              },
      

    打开日志离线保存功能后,DIAG日志不再输出到串口,而是保存到文件中。

    关闭日志离线保存功能后,DIAG日志直接输出到串口。

    说明: 使用方法一和方法三,每次启动时,离线日志自动生效。方法二,只是临时生效,复位后会恢复。

  4. 从“/user/log/”目录导出所有日志文件。其中包含:

    • 一个日志索引文件(log_file_diag.bin.idx)
    • 若干个日志数据文件(如log_file_diag.bin.0001、log_file_diag.bin.0002等)
  5. 将日志文件拖入到DebugKits工具,可以查看日志内容(详情请参见《DebugKits工具 使用指南》)。

说明: 保存日志的空间有大小限制,目前总大小为20MB,日志保存空间达到最大值后,会覆盖最早的日志继续保存。

代码示例
#include "diag_log.h"
int main()
{
    uapi_zdiag_set_offline_log_enable(true);
    uapi_diag_info_log(0, "test out value = %d", 1);
    uapi_diag_error_log(0, "test out error a = %d, b = %d\r\n", 3, 4);
}

命令注册功能

场景说明

用户可以注册自定义命令,在DebugKits工具的命令行界面输入命令与单板侧进行交互,执行用户自定义的操作。

工作流程

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

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

  1. 在芯片命令 ID 头文件 /src/middleware/chips/3322/dfx/include/soc_diag_cmd_id.h 中定义命令 ID。通用命令及命令 ID 组合方式见 /src/middleware/utils/dfx/diag/include/diag_cmd_id.h

    Hi3322 的芯片自定义命令使用 0xC00xEF,其中 0xC00xCF 为系统指标命令段。以下示例选用当前命令 ID 头文件和命令数据库中均未占用的 0xD0

    代码示例如下:

    #define DIAG_CMD_GET_USER_INFO             diag_cmd_id_comb(0xD0)
    
  2. 调用“uapi_diag_register_cmd”函数,注册该命令的回调函数。

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

    代码示例如下:

    diag_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_diag_register_cmd(g_diag_user_cmd_tbl,
            sizeof(g_diag_user_cmd_tbl) / sizeof(g_diag_user_cmd_tbl[0]));
    }
    
  3. 注册 DFX 命令步骤中注册的回调函数“diag_cmd_get_user_info”中,实现收到该命令后的操作,如果需要给DebugKits工具应答,则调用“uapi_diag_report_packet”函数上报应答报文。

    代码示例如下:

    typedef struct {
        uint32_t user_value;
    } user_info_t;
    
    static errcode_t diag_cmd_get_user_info(uint16_t cmd_id, void *cmd_param,
        uint16_t cmd_param_size, diag_option_t *option)
    {
        user_info_t info = {0};
    
        (void)cmd_param;
        (void)cmd_param_size;
        /* 在这里填充应用实际需要上报的数据。 */
        return uapi_diag_report_packet(cmd_id, option, (const uint8_t *)&info,
            (uint16_t)sizeof(info), true);
    }
    
  4. /src/build/config/target_config/3322/hdb_config/database_template/acore/system/hdbcfg/mss_cmd_db.xml 中定义命令。XML 中的命令 ID 按现有数据库格式填写;diag_cmd_id_comb(0xD0) 对应 0xD005。代码示例如下(CMD标签部分为需要新增内容):

    <DebugKits>
      <GROUP NAME="FIX" DATA_STRUCT_FILE="..\diag\fix_struct_def.txt" MULTIMODE="Firefly" AUTO_STRUCT="YES" PLUGIN="0x111,0x110(1),0x252">
        <CMD ID="0xD005" NAME="get_user_info" DESCRIPTION="get_user_info" PLUGIN="0x100" TYPE="REQ_IND">
          <REQ STRUCTURE="tool_null_stru" TYPE="Auto" PARAM_VALUE="" />
          <IND STRUCTURE="user_info_t" TYPE="Auto" RESULT_CODE="" />
        </CMD>
      </GROUP>
    </DebugKits>
    
  5. 参照开发环境快速入门完成构建,将生成的“output/3322/database_evb”数据库更新至DebugKits工具数据库中(参照“DebugKits使用说明”中的更新数据库),打开DebugKits命令行界面,即可输入“get_user_info”命令,实现获取用户数据信息的功能。

Database修改

上述工作流程中配置命令数据库步骤修改的“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为0xD005。 */
        <CMD ID="0xD005" 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="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;
    } user_info_t;
    

系统维测接口

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

  • 内存使用统计接口
  • 任务信息统计接口

内存使用统计

概述

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

功能描述

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

图 1 内存统计使用示例

**图 1** 内存统计使用示例

命令行代码示例

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_diag_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 任务信息统计示例

**图 1** 任务信息统计示例

命令行代码示例

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_diag_report_packet(cmd_id, option, (uint8_t *)inf, (uint16_t)sizeof(task_info_t), true);
        }
    }
    dfx_free(0, infs);
    return ERRCODE_SUCC;
}

CHR打点功能

场景说明

在某些行为需要被记录时,可以通过调用CHR打点接口(支持单核或者多核打点)将打点数据保存到文件系统中,在需要的时候将CHR打点文件导出到PC上,通过DebugKits工具进行解析。

工作流程

以用户在“/src/application/3322/3322_app_standard/thread_init.c”中增加CHR打点日志为例,流程如下:

  1. 在“/src/application/3322/3322_app_standard/thread_init.c”中调用CHR打点接口添加event_xxx时,输出调试信息。thread_init.c中需要包含“massdata_buffer.h”头文件,需要该头文件中的massdata_event_id中添加event_xxx,并且自定义info1/info2/info3事件。

    表 1 CHR打点接口

    函数

    说明

    uapi_massdata_record_system_event

    记录打点事件,最多支持以下四个参数event_id/info1/info2/info3,参数的层级关系如图1所示。

    uapi_massdata_record_system_long_event

    记录打点事件,最多支持以下四个参数event_id/info1/info2/info3,参数的层级关系如图1所示。其中info3支持可变长度的buf,需要在入参中输入info3的长度,最大为128Byte。

    图 1 打点信息关系示意图

    **图 1** 打点信息关系示意图

  2. 打开/关闭CHR打点功能特性方法:

    特性宏:dfx_feature_config.h

    示例如下:

    /
    /* 特性: 支持大数据打点 */
    #ifndef CONFIG_DFX_SUPPORT_MASSDATA_DOTTING
    #define CONFIG_DFX_SUPPORT_MASSDATA_DOTTING         DFX_YES
    #endif
    

    在此文件中将“CONFIG_DFX_SUPPORT_MASSDATA_DOTTING”的值置为“DFX_YES”离线日志存储功能特性打开;置为“DFX_NO”,功能特性关闭。

  3. 打开/关闭CHR打点功能的方法如下:

    • 方法一:在代码中调用“uapi_massdata_set_state”函数。
    • 方法二:在DebugKits的命令行界面输入“diag_dfx 9,[0,2,0]”命令:

      diag_dfx 9,[0,2,0]:打开CHR打点功能。

      diag_dfx 9.[0,0,0]:关闭CHR打点功能。

      diag\_dfx 9.\[0,0,0\]:关闭CHR打点功能

说明: 保存日志的空间有大小限制,目前总大小为96KB,日志保存空间达到最大值后,会覆盖最早的日志继续保存。

代码示例
#include "massdata_buffer.h"
typedef enum {
    MT_CASE1 = 0x0,
    MT_CASE2 = 0x1,
} massdata_xxx_info1;
typedef enum {
    MT_CASE1_STEP1 = 0x0,
    MT_CASE1_STEP2 = 0x1,
} massdata_xxx_CASE1_info2;
int main()
{
    uapi_massdata_record_system_event(event_xxx, MT_CASE1, MT_CASE1_STEP1, 0);
    uapi_massdata_record_system_event(event_xxx, MT_CASE1, MT_CASE1_STEP2, 0);
}

故障诊断与数据分析

本章介绍死机信息、Last Dump 和内存 DFX 数据的获取、解析与定位方法。

死机诊断

简介

死机诊断是指HiDiTingV100在发生异常时能够记录并输出系统异常信息,后统称死机信息。其中包括死机现场的PC地址、返回地址、栈地址、CPU寄存器信息以及部分内存信息等。用户可根据实际使用场景进行获取并通过分析定位根因,解决死机问题。

HiDiTingV100支持以下表1所示几种方式记录并输出死机信息。

表 1 死机信息类型说明

类型

说明

依赖

适用场景

所在章节

串口死机信息

死机时在串口直接输出死机信息

串口

适用于未关闭串口的情况。可以获取死机时异常信息、CPU寄存器信息、函数调用栈信息等。

串口工具界面输出串口死机信息查看

Flash死机文件

死机时在Flash中保存死机信息

Flash

适用于产品阶段,关闭串口输出的情况。可以获取死机时异常信息、CPU寄存器信息、函数调用栈信息等。

Flash中的死机文件获取Flash中的死机文件信息查看

Last word

死机时将死机的临终遗言输出到DebugKits工具

Debug串口,DebugKits工具

适用于未关闭Debug串口的情况。可以获取死机时PC地址、返回地址、栈地址、CPU寄存器值等信息。

获取Last word信息Last word信息查看

Last dump

死机时将当时的部分内存和寄存器信息输出到DebugKits工具,并保存到Flash中

Debug串口,DebugKits工具

适用于未关闭Debug串口的情况。可以获取到死机时的大部分内存的原始数据,以及部分关键寄存器的信息,现场数据比较丰富,适用于定位一些踩内存等比较难定位的问题。

获取Last dump信息Last dump信息查看

J-Link调试信息

死机时连接J-Link查看现场信息

J-Link

适用于开发初期,单板需要能够连接J-Link调试器。可以获取大量现场数据(如内存、寄存器、flash数据等)。

连接J-Link调试器导出连接J-Link调试器查看信息

死机信息获取

本节主要介绍“表1”中的几种死机信息的获取方式:

  • 通过串口工具获取死机打印信息。
  • 通过保存在Flash中的死机文件获取死机信息。
  • 通过DebugKits工具获取死机信息。

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

适用场景:

  • 在研发调测场景下,上述四种方法均适用。
  • 在外网场景下适用通过DebugKits工具或者Flash中的死机文件来获取死机信息。

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

串口工具界面输出

单板与PC通过串口连接之后,打开任意串口工具(SSCOM/XSHELL/IPOP/MobaXterm等)界面。选择对应的端口号之后点击打开串口进行连接,波特率选择115200,如图1所示。

图 1 串口连接

**图 1** 串口连接

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

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

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

Flash中的死机文件获取

在发生死机异常时,系统会同时将死机信息以死机文件的形式保存在Flash文件系统中。

  • APP核死机文件路径:/user/exc/app_exc_info.bin
  • BT核死机文件路径:/user/exc/bt_exc_info.bin

死机文件可使用DebugKits工具导出,步骤如下:

(DebugKits详细操作方法请参见“DebugKits使用说明”以及《DebugKits工具 使用指南》

图 1 死机文件导出方法

**图 1** 死机文件导出方法

  1. 在标注①处选好死机文件导出存放位置。
  2. 在标注②处填写“/user/exc”。
  3. 在标注⑤处点击“Get File List”按钮获取该目录下所有文件,在标注③处选择需要下载的死机文件。
  4. 在标记④处点击“Download”或“Download All”按钮,死机文件将下载至标注①选择的目录位置。

通过DebugKits工具获取

在死机故障发生时,如果DebugKits处于连接状态,会获取到两种死机信息:

  • Last word信息
  • Last dump信息

参照“DebugKits使用说明”中的1~4,打开DebugKits工具的消息界面,可以实时观测芯片运行时打印的具体信息,如图1所示。

当死机发生时消息界面将会打印出Last word以及Last dump信息。

图 1 DebugKits日志打印

**图 1** DebugKits日志打印

获取Last word信息

Last word信息是死机发生时的临终遗言,其中包括PC地址、返回地址、栈地址、CPU寄存器值等信息。用户可以根据Last word的具体信息分析错误类型、定位错误原因。

在DebugKits的Message界面,点击Last word那一行,即可查看Last word具体信息,其中,A核的Last word和BT核的Last word有所不同,A核的Last word信息如图1所示。BT核Last word信息如图2所示。

图 1 A核Last word信息

**图 1** A核Last word信息

图 2 BT核Last word信息

**图 2** BT核Last word信息

A核和BT核的Last word信息差异在于:

  • 在message界面中名称不同:A核名称为last_word,BT核名称为EXCEPTION_LAST_RUN_INFO。
  • 内容中reg_value的个数不同和对应的含义不同:A核reg_value为32个,BT核reg_value为13个。其对应的含义详见“Last word信息查看”章节。

获取Last dump信息
  • Last dump信息是在死机故障发生时,将单板中的部分内存和寄存器信息通过串口发送到PC端,通过DebugKits工具生成bin文件存储到的安装目录中的DumpInfo子目录之下,具体参考图1

    用户可以通过解析这些文件来定位死机原因。解析这些文件的方法请参见“Last Dump解析”。

    图 1 Last dump生成文件

    **图 1** Last dump生成文件

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

  • 死机故障发生时,如果单板没有连接debugkits,系统会同时将Last dump信息以bin文件的形式保存在Flash文件系统中。

    • APP核死机文件路径:/user/exc/APP_ITCM_ORIGIN.bin、/user/exc/APP_DTCM_ORIGIN.bin、/user/exc/SHARE_MEM.bin、/user/exc/MCPU_TRACE_MEM_REGION.bin
    • BT核死机文件路径:/user/exc/BT_RAM_ORIGIN、/user/exc/SHARE_MEM.bin、/user/exc/BCPU_TRACE_MEM_REGION.bin

连接J-Link调试器导出

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

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

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

硬件连接后,双击仓库中的 Commandline_hi3322_mcpu.bat 脚本,脚本将自动进行连接(其他核的连接操作可参照此方法进行)。

图 1 J-Link连接MCPU示意图

**图 1** J-Link连接MCPU示意图

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

死机信息查看

对于“死机信息获取”中描述的几种方式获取的死机信息,本节分别介绍它们的查看方式和定位方法。

串口死机信息查看

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

异常信息汇总

表 1 异常信息汇总

成员

说明

task

死机的任务名称。

thrdPid

死机的任务ID。

type

死机类型,type = mcause & 0xFFF。

CPU寄存器信息

表 1 死机相关CPU寄存器说明

成员

说明

mepc

机器异常程序计数器。当发生异常时,mepc指向导致异常的指令;对于中断,mepc指向中断处理后应该恢复的位置。

mstatus

机器状态寄存器。

mtval

机器陷入寄存器。保存地址异常中出错的地址或者发生指令异常的指令本身,对于其他错误,其值为零。

mcause

机器异常寄存器。保存目前异常或者中断的原因,通过查询表2得到目前异常或者中断的类型。表格中的异常码 = mcause & 0xFFF。

ccause

ccause为mcause的补充说明,对于某些异常通过读取ccause寄存器的内容可以进一步明确异常类型。

图 1 死机相关CPU寄存器说明

**图 1** 死机相关CPU寄存器说明

表 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

0xFFF

NMI interrupt

Not available

函数调用栈信息

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

用户可以到“output/3322/acore/diting-community/application.lst”中找到对应的函数。

Flash中的死机文件信息查看

按照“Flash中的死机文件获取”中的描述,从Flash中导出的死机文件为二进制文件,需使用死机信息解析脚本(dump_parser)解析后查看。

死机信息解析脚本支持在Windows和Linux环境下运行,可解析从Flash中读出的A核和BT核死机信息。

  • Linux运行方式:进入脚本目录给予脚本可执行权限并运行下列命令,结果如图1所示。

    chmod +x run_hi3322.sh

    ./run_hi3322.sh <死机信息文件路径> [lst 文件路径(可选)]

    图 1 Linux环境运行死机脚本工具

    Linux死机分析结果上半部分

    Linux死机分析结果下半部分

  • Windows运行方式:在脚本目录下打开命令提示符,运行下列命令即可开始解析,结果如图2所示。

    run.bat <死机信息文件路径> [lst 文件路径(可选)]

    图 2 Windows环境运行死机脚本工具

    Windows死机分析结果上半部分

    Windows死机分析结果下半部分

    Flash死机文件解析后的具体内容以及含义,A核的死机信息可参考“串口死机信息查看”;BT核的死机信息,可参考“Last word信息查看”。

说明: 使用中的常见问题:

  • 死机文件解析脚本需要 Python 3.8 及以上版本,安装界面与环境检查方法参见开发环境快速入门
  • 脚本依赖 pycparser,版本与获取方式以项目页为准。

通过DebugKits获取的死机信息查看

Last word信息查看

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

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

表 1 Last word内容

变量名称

具体含义

stack_limit

系统栈大小。

fault_type

错误类型(mcause)。其中NMI interrupt在A核为0x80000FFF,在BT核为0x8000000C。

fault_address

错误地址,属性值无意义。

fault_reason

错误原因,属性值无意义。

reg_value

保存寄存器值。

psp_value

栈指针(sp)。

lr_value

返回地址(ra)。

pc_value

发生错误的指令地址(mepc)。

psps_value

全局指针(gp)。

primask_value

异常状态寄存器(mstatus)。

fault_mask_value

CPU访问异常地址或异常值(mtval)。

bserpri_value

自定义异常状态寄存器(ccause)。

control_value

CPU访问异常地址或异常值(mtval)。

对于Last word内容中保存的寄存器的值(reg_value),其个数和具体含义A核和BT核有所不同。

具体含义如表2表3所示。

表 2 A核Last word中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)

表 3 BT核Last word中reg_value成员含义

成员名称

成员含义

reg_value[0]

函数参数/返回值(a0)

reg_value[1]

函数参数/返回值(a1)

reg_value[2]

函数参数(a2)

reg_value[3]

函数参数(a3)

reg_value[4]

函数参数(a4)

reg_value[5]

函数参数(a5)

reg_value[6]

函数参数(a6)

reg_value[7]

函数参数(a7)

reg_value[8]

被调函数需要保存用的寄存器/调用栈的帧指针(s0)

reg_value[9]

被调函数需要保存用的寄存器(s1)

reg_value[10]

被调函数需要保存用的寄存器(s10)

reg_value[11]

被调函数需要保存用的寄存器(s11)

reg_value[12]

被调函数需要保存用的寄存器(s2)

Last dump信息查看

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

在解析生成的“memory_result.txt”中搜索“死机”可查询到死机信息,如图1所示。

图 1 Last dump中的死机信息

**图 1** Last dump中的死机信息

其中的内容与串口中的死机信息类似(如图2所示),不同的是,Last dump的死机信息中函数调用栈中的地址会自动找到对应的函数。

连接J-Link调试器查看信息

本小节主要介绍在死机现场使用J-Link调试器导出死机信息的过程。

通过“连接J-Link调试器导出”的方法连接J-Link后,可使用表1中的命令查看当前的状态和信息,如:寄存器、内存、flash中的数据。

表 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寄存器信息,如图1所示。

    图 1 查询CPU寄存器值

    **图 1** 查询CPU寄存器值

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

    1. 在“\output\3322\acore\3322-xxx\xxx.nm”中获取nm文件,在nm文件中获取想要查询的变量地址如图2中“g_exception_dump_callback”变量的地址为0x20025950。

      图 2 变量地址

      **图 2** 变量地址

    2. 通过J-Link获取状态信息,如图3所示。

      图 3 变量值

      **图 3** 变量值

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

常见死机问题定位

看门狗重启问题定位

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

表 1 看门狗死机关键信息

成员

说明

type/mcause

看门狗死机,死机类型为0xFFF。

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指向的语句可能不是异常语句。
  • RISCV lb、lh、lw、ld、sb、sh、sw、sd指令要求访问地址字节对齐。

    • lb(load byte):对齐要求为1字节。
    • lh(load halfword):对齐要求为2字节。
    • lw(load word):对齐要求为4字节。
    • ld(load doubleword):对齐要求为8字节。
    • sb(store byte):对齐要求为1字节。
    • sh(store halfword):对齐要求为2字节。
    • sw(store word):对齐要求为4字节。
    • sd(store doubleword):对齐要求为8字节。

    因此,对于load和store指令,被访问的地址不满足对应数据长度的对齐要求,将会引发地址对齐异常,建议客户在定义全局变量时,使用__attribute__((aligned(8)))进行对齐。

取指异常

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

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

  • 存储函数跳转地址的钩子变量被踩,导致函数跳转到非法地址。
  • 栈被踩,导致函数无法正确返回上一级函数而跑飞。
  • 代码段本身被踩。

Last Dump解析

概述

Last Dump解析功能是指将死机发生时导出的内存数据的内容进行解析,将.bin文件解析为可读性较好的文本文件,为问题定位提供参考信息。

功能描述

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

  • 第一部分主要功能是解析DWAEF文件的“.debug_info”段,将对应的tag解析生成python格式的class文件,同时解析符号表,获取全局变量信息。此步骤当前集成在版本构建流程中。
  • 第二部分主要功能是将第一步生成的文件作为输入,解析死机dump文件,生成文本信息。此步骤当前集成在DebugKits工具中。

场景说明

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

工作流程
  1. 死机dump文件获取,请参考“获取Last dump信息”。其中,DebugKits工具导出的Last dump生成文件就是待解析的死机文件(Last Dump功能是否开启通过DUMP_MEM_SUPPORT、DUMP_REG_SUPPORT、SUPPORT_DFX_EXCEPTION这三个宏控制)。
  2. 编译构建生成解析工具。上文已经介绍了解析功能的第一步集成在版本构建流程中,Last Dump解析前需要先进行版本编译构建获取parse_tool解析工具。

    1. /src/build/config/target_config/3322/target_config.pytarget_standard_3322_application_template 配置中,将 gen_parse_toolFalse 改为 True
    2. 开发环境快速入门完成版本编译。Last Dump 解析所需脚本和中间文件在构建过程中生成;脚本源文件见 /src/build/script/parse_tool/。生成成功时,构建日志会提示“Build parse tool success”。
    3. 编译结果确认,在output目录“output/3322/acore/<target-name>/”下生成“application.nm”文件、“application.llvminfo”文件以及“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_6_phase2.py:Liteos208版本内存解析脚本的第二阶段入口,对应Liteos208版本的解析,在DebugKits工具界面调用,用于将B核的全内存.bin文件解析为用户可见的文本信息。
      • parse_main_liteos208_8_phase2.py:Liteos208版本内存解析脚本的第二阶段入口,对应Liteos208版本的解析,在DebugKits工具界面调用,用于将A核的全内存.bin文件解析为用户可见的文本信息。
      • parse_main_freertos_phase2.py:FreeRTOS版本内存解析脚本的第二阶段入口,对应FreeRTOS版本的解析,在DebugKits工具界面调用,用于将全内存.bin文件解析为用户可见的文本信息。
      • parse_print_global_var.py:内存解析脚本。
      • xml_main.py:内存解析脚本。
  3. 执行Last Dump解析。在执行Last Dump解析之前,确保运行解析工具的PC上已安装python3和DebugKits工具。

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

      图 1 DebugKits工具Dump解析界面

      **图 1** DebugKits工具Dump解析界面

      • python file是解析脚本的路径,需要用户选取,例如“/output/3322/acore/<target-name>/parse_tool/parse_main_****_phase2.py”。根据固件使用的OS选择对应脚本:LiteOS 206、207、208(6/8)分别使用同名版本的 parse_main_liteos*_phase2.py,FreeRTOS使用 parse_main_freertos_phase2.py;仓内脚本源文件见 /src/build/script/parse_tool/
      • NM file为内存解析脚本的输入文件,在版本构建时通过编译命令解析elf文件生成,路径:/output/3322/acore/<target-name>/application.nm。
      • Global path为内存解析时需要导入的全局变量信息,在版本构建时通过解析脚本的第一步生成,路径:/output/3322/acore/<target-name>/parse_tool/global.txt。
      • bin list为待解析的内存文件路径(即通过DebugKits工具导出的内存bin文件的存储路径:例如:D:\DebugKits\DumpInfo\DumpInfo_1),只需要选取路径即可,工具会自动加载该路径下相关的.bin文件。
      • Output file为解析结果文件存放路径,需要用户选取,例如“D:\DebugKits\DumpInfo\DumpInfo_1\memory_result.txt”。
    2. 单击Dump Analyze界面的“Execute”按钮,正常情况下DebugKits工具界面会显示解析结果,并且在用户选取的Output file路径下会生成“memory_result.txt”解析结果如图2所示,如果没有显示结果,则参考“异常处理”的介绍进行处理。

      图 2 Last Dump解析结果文件

      **图 2** Last Dump解析结果文件

    3. 分析解析结果。

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

      图 3 Last Dump解析结果片段

      **图 3** Last Dump解析结果片段

异常处理
  1. 如果执行Last Dump解析后DebugKits工具没有显示解析结果,首先需要确认“application.nm”文件、“application.llvminfo”文件、“parse_tool”脚本及导出的“.bin”文件是否出自同一个软件版本。不同版本的输入不匹配可能会导致异常。
  2. DebugKits工具导入待解析的.bin文件时选取的是整个目录,要保证DebugKits工具配置文件“\DebugKits\Source-Datas\bin\config\config.ini”中的配置与DumpInfo文件夹下导出文件的地址及大小一致,否则也会导致解析异常。

    可以将“config.ini”中的 DumpInfo 配置项与源码 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中配置信息

    **图 1** config.ini中配置信息

    图 2 g_app_mem_dump_info和g_bt_mem_dump_info数组

    内存转储信息数组

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

    方法如下:

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

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

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

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

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

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

内存DFX功能

概述

内存DFX功能主要用于内存调优分析,主要包括系统内存占用分析、各任务内存占用分析、任务栈使用率分析以及内存泄漏检测。

功能描述

须知: 所有命令在执行时均会关中断,因此可能导致消息队列满等异常,建议尽量避免在压测场景下使用“AT^SHOWALLTSKHEAP 、AT^SHOWMEMINFO”指令,这两个指令均可以用其他指令组合后替代。

查询指定任务内存占用情况
  1. 使用任意串口工具下发“AT^SHOWTSKHEAP=<taskId>”命令,taskId的获取请参见“查询任务信息”,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令下发后可以得到四个信息。

    ①当前任务占用内存的详细信息,size表示一个内存节点的大小,blockCount表示已分配的内存块数量,callerRA表示该内存节点申请时的下一条指令地址。

    ②当前任务占用的非slab内存总量。

    ③当前任务占用的slab内存总量。

    ④当前系统中因为内存节点申请而带来的总内存开销。

    具体内容如图2所示。

    图 2 命令执行结果

    **图 2** 命令执行结果

查询当前所有任务内存占用情况
  1. 使用任意串口工具下发“AT^SHOWALLTSKHEAP”命令,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令下发后可以得到当前系统内所有任务的四个信息。

    ①当前任务占用内存的详细信息,size表示一个内存节点的大小,blockCount表示已分配的内存块数量,callerRA表示该内存节点申请时的下一条指令地址。

    ②当前任务占用的非slab内存总量。

    ③当前任务占用的slab内存总量。

    ④当前系统中因为内存节点申请而带来的总内存开销。

    具内容如图2所示。

    图 2 命令执行结果

    **图 2** 命令执行结果

查询当前所有空闲内存信息
  1. 使用任意串口工具下发“AT^SHOWMEMFREE”命令,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令下发后可以得到当前系统空闲内存的四个信息:

    ①size:空闲内存块的大小。

    ②from:该空闲块的起始地址。

    ③end:该空闲块的结束地址。

    ④当前总内存剩余量。

    具体内容如图2所示。

    图 2 命令执行结果

    **图 2** 命令执行结果

查询当前内存统计信息
  1. 使用任意串口工具下发“AT^SHOWHEAPST”命令,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令下发后可以得到三类信息:

    ①当前内存的使用信息,total表示内存的总量,used表示当前已使用的内存量,current free表示当前剩余内存量,peak usage表示峰值内存使用量,peak free表示峰值内存剩余量。

    ②当前所有任务内存分配信息,current malloc表示截止统计时任务依旧持有的内存量,peak malloc表示截止统计时任务所持有内存的最大值。

    ③非任务所占用的内存,比如中断等。

    具体内容如图2所示。

    图 2 命令执行结果

    **图 2** 命令执行结果

查询内存的所有信息
  1. 使用任意串口工具下发“AT^SHOWMEMINFO”命令,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 该命令执行结果包含“ATSHOWTSKWTL、ATSHOWHEAPST、ATSHOWALLTSKHEAP、ATSHOWMEMFREE”四个命令的执行结果,详情请参考各命令执行结果。

查询任务信息
  1. 使用任意串口工具下发“AT^SHOWMEMINFO”命令,以IPOP工具为例,如图1所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令执行结果包括所有任务的任务名及taskId,如图2所示。

    图 2 命令执行结果

    **图 2** 命令执行结果

查询任务水线

  1. 使用任意串口工具下发“AT^SHOWTSKWTL”命令,以IPOP工具为例,如图1 所示。

    图 1 串口命令

    **图 1** 串口命令

  2. 命令执行结果包含所有任务的任务栈分配情况如图2所示。

    参数说明如下:

    stackTop:任务栈初始化时的栈顶;

    stackLen:任务栈大小;

    peakUsage:峰值任务栈使用量;

    sp:当前任务栈栈顶;

    peakRatio:任务栈峰值使用率。

    图 2 命令执行结果

    **图 2** 命令执行结果

DebugKits 工具使用

DebugKits使用说明

DIAG 大部分维测功能都需要配合 DebugKits 使用。下面介绍连接、数据库更新、日志查看和命令执行等常用操作,完整功能参见《DebugKits工具 使用指南》

  1. 进入工具页面,单击“Options”再选择“Change Chip”,在弹框中选择芯片类型,如图1所示。

    图 1 选择芯片

    **图 1** 选择芯片

  2. 单击“Connection”选择“Connect”在弹窗中选择连接方式以及对应的通道序号进行连接,如图2所示。

    图 2 连接芯片

    **图 2** 连接芯片

  3. 更新数据库。如果是第一次连接芯片或者芯片有新版本的程序,需要更新HDB并重新连接之后,日志的打印才能正确的显示,在Option菜单下选择“Update HDB”,选择生成的database目录(output\3322\database_evb)再单击“OK”即可完成配置,具体如图3所示。

    图 3 配置database

    **图 3** 配置database

  4. 消息界面。单击菜单栏或工具栏上的命令行图标,可打开消息界面查看日志。

    图 4 打开消息界面

    **图 4** 打开消息界面

    图 5 查看日志

    **图 5** 查看日志

  5. 命令行界面。单击菜单栏或工具栏上的命令行图标,可打开命令行界面。

    图 6 打开命令行界面

    **图 6** 打开命令行界面

    图 7 命令输入

    **图 7** 命令输入

问题处理与注意事项

注意事项

  • DIAG 日志宏当前仅支持日志 ID 0,可变参数最多 10 个;优化日志参数使用不超过 32 位的数值类型,避免字符串、浮点数、long 或 64 位参数。
  • 固件、database_evb 与 DebugKits 连接必须来自同一轮构建,否则日志可能无法解码。
  • 自定义命令必须检查 diag_cmd_id.h、芯片命令 ID 头文件与 mss_cmd_db.xml,不要把某个 ID 区间视为可直接复用范围。
  • 离线日志和 CHR 的可用容量受功能配置与存储布局影响,应按当前目标配置和工具实际显示结果评估。
  • 死机、Last Dump、内存 DFX 的采集和解析遵循对应章节中的固件、数据库与工具版本要求。

问题处理

现象 原因与处理
*_C 未声明 日志文件 ID 未登记到所选模块的 log_def_*.h。登记枚举项后重新构建。
新增的 DFX 日志代码未进入固件 确认承载代码的业务组件已进入当前目标的组件列表,并检查组件的源文件列表、日志模块和文件 ID 配置。
diag_log.happ_init.h 找不到 保持原生样例 CMake 的头文件配置,并使用 SDK 标准构建入口。
DebugKits 只显示消息 ID 更新与当前固件同一次构建生成的 database_evb,重新连接设备。
自定义命令不可见或返回异常 同时检查命令注册、命令 ID、mss_cmd_db.xml 和数据库更新。

问题反馈

反馈 DFX 问题时,建议提供芯片型号、目标名称、固件版本、DebugKits 版本、匹配的 database_evb、复现步骤和相关日志。