前言

概述

本文档主要介绍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信息可能无法完整获取。

死机问题定位

串口死机信息查看

在死机故障发生时,一般会在串口输出串口界面输出中描述的死机信息。其中包含几个部分:异常信息汇总、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解析在命令行窗口执行结果