前言¶
概述
本文档主要介绍BS2X DFX功能及使用指南,包括DIAG维测功能、死机诊断、Dump解析等,方便用户进行业务维测及死机分析。
产品版本
与本文档相对应的产品版本如下。
产品名称 |
产品版本 |
|---|---|
BS2X |
V100 |
读者对象
本文档主要适用于以下工程师:
软件工程师
技术支持工程师
符号约定
在本文中可能出现下列标志,它们所代表的含义如下。
符号 |
说明 |
|---|---|
|
表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。 |
|
表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。 |
|
表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。 |
|
用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。 |
|
对正文中重点信息的补充说明。 “说明”不是安全警示信息,不涉及人身、设备及环境伤害信息。 |
修改记录
文档版本 |
发布日期 |
修改说明 |
|---|---|---|
04 |
2025-08-07 |
更新“代码示例”小节内容。 |
03 |
2025-03-26 |
更新“系统维测接口”小节内容。 |
02 |
2024-07-04 |
|
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中增加调试日志为例,流程如下:
在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个参数。
用户在使用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)的日志。
定义该文件的文件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。
编译程序,编译过程中将生成output/bx2x/database_evb目录。
在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”命令为例,流程如下:
在middleware/chips/bx2x/dfx/include/soc_diag_cmd_id.h文件中定义命令ID,注意不要与已存在的命令ID重复。
代码示例如下:
#define DIAG_CMD_GET_USER_INFO 0x8011调用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])); }
在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; }
在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>
将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信息
查询CPU寄存器信息,如图2所示。
图 2 查询CPU寄存器值

查询内存信息,获取维测变量值或普通变量值。
说明: 以上地址信息仅作为演示使用,查询步骤供用户参考使用。
死机问题定位¶
串口死机信息查看¶
在死机故障发生时,一般会在串口输出串口界面输出中描述的死机信息。其中包含几个部分:异常信息汇总、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值是否合法。
|
说明: 由于存储存在异步性,存储异常时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解析功能,为死机问题定位提供更多的参考信息。
工作流程¶
死机dump文件获取,请参考“DebugKits工具获取信息”。其中,DebugKits工具导出的last dump生成文件就是待解析的死机文件(last dump功能是否开启通过DUMP_NUM_SUPPORT、DUMP_REG_SUPPORT、SUPPORT_DFX_EXCEPTION这三个宏控制)。
编译构建生成解析工具。上文已经介绍了解析功能的第一步集成在版本构建流程中,dump解析前需要先进行版本编译构建获取parse_tool解析工具。
获取源码进行版本编译,编译环境需要支持python3,dump解析功能所需脚本和中间文件在版本编译过程中生成,编译过程中会提示信息:Build parse tool success。
编译结果确认,在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 内存解析脚本
执行dump解析。在执行dump解析之前,确保运行解析工具的PC上已安装python3和DebugKits工具。
在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文件。
点击Dump Analys界面的Analys按钮,正常情况下DebugKits工具界面会显示解析结果,并且File Path路径(即工具选取的内存.bin文件路径)下会生成memory_result.txt解析结果,如果没有显示结果,则参考“异常处理”的介绍进行处理。
图 2 Dump解析结果文件

分析解析结果。
打开memory_result.txt文件可以查询到当前的中断信息、任务信息、消息队列、信号量、全局变量、死机信息等。
图 3 Dump解析结果片段

异常处理¶
如果执行Dump解析后DebugKits工具没有显示解析结果,首先需要确认application.nm、application.info文件、parse_tool脚本及导出的.bin文件是否出自同一个软件版本。不同版本的输入不匹配可能会导致异常。
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数组

上述两点都排查无误后,需要手工执行脚本进行问题定位。
方法如下:








