前言¶
本文档主要介绍BS2X DFX功能及使用指南,包括DIAG维测功能、死机诊断、Dump解析等,方便用户进行业务维测及死机分析。
与本文档相对应的产品版本如下。
本文档主要适用于以下工程师:
软件工程师
技术支持工程师
在本文中可能出现下列标志,它们所代表的含义如下。
更新“代码示例”小节内容。 |
||
更新“系统维测接口”小节内容。 |
||
|
||
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 日志打印接口
用户在使用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所示。
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所示。
当硬件与串口工具处于连接状态时,如果单板发生死机,串口工具能够接收到复位前OS打印的死机信息。如图2所示。
DebugKits工具获取信息¶
首先进入工具页面点击options再选择change chip,在弹框中选择BS2X确定芯片类型,参考图1。
然后点击Connection选择Connect在弹窗中选择连接方式以及对应的通道序号进行连接,参考图3。
如果是第一次连接芯片或者芯片有新版本的程序,需要更新HDB并重新连接之后,日志的打印才能正确的显示,在Option菜单下选择Update HDB,选择生成的database目录(\output\bx2x\database_evb)再点击OK即可完成配置,具体参考图4。
通过DebugKits工具可以实时观测芯片运行时打印的具体信息,可以参考图5。当死机发生时Message视图将会打印出last word以及last dump信息。
last word信息是死机发生时,将PC地址、返回地址、栈地址、CPU寄存器信息发送出来,用户可以根据last word的具体信息分析错误类型、定位错误原因,具体可以参考图6。
last dump信息将会把内存中的部分信息打包生成bin文件存储到DebugKits的安装目录中的DumpInfo子目录之下,具体参考图7。用户可以通过解析这些文件定位错误位置。使用方法详见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脚本,脚本将自动进行连接(其他核的连接操作可参照此方法进行)。
须知: 注意RISC-V架构处理器需要使用V10及以上版本的J-Link仿真器。
表 1 J-Link常用命令
I/O读32bit:mem32 <Addr>, <NumItems> (hex) (Addr表示内存地址 NumItems表示从Addr开始连续读几个32bit ) |
|
I/O写4Byte:w4 <Addr>, <Data> (hex) |
|
查询CPU寄存器信息,如图2所示。
查询内存信息,获取维测变量值或普通变量值。
说明: 以上地址信息仅作为演示使用,查询步骤供用户参考使用。
死机问题定位¶
串口死机信息查看¶
在死机故障发生时,一般会在串口输出串口界面输出中描述的死机信息。其中包含几个部分:异常信息汇总、CPU寄存器信息、函数调用栈信息。
异常信息汇总¶
表 1 异常信息汇总
CPU寄存器信息¶
表 1 死机相关CPU寄存器描述
机器异常寄存器。保存目前异常或者中断的原因,通过查询表2得到目前异常或者中断的类型。 |
|
与mcause类似,ccause为mcause的补充说明,对于某些异常通过读取ccause寄存器的内容可以进一步明确异常类型。 |
表 2 mcause&ccause异常描述表
函数调用栈信息¶
函数调用栈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内容
对于last word上面内容里面保存的寄存器的值(reg_value)具体含义如表2所示。
表 2 reg_value成员含义
内存last dump信息查看¶
如“DebugKits工具获取信息”中描述,当死机故障发生时,同时会将当前的内存dump到DebugKits保存下来,并可使用相关工具做进一步解析。详细使用方法请参见“Dump解析”。
看门狗重启问题定位¶
看门狗死机导致的重启问题,大概率为代码中存在死循环(包含次数很大的循环)或某个业务一直被执行(例如灌包),使得IDLE任务无法得到调度,规定时间内未进行喂狗造成的重启。通常可以通过表1中的信息并结合lst文件,确定具体的死机位置。
表 1 看门狗死机关键信息
CPU异常触发重启问题定位¶
通过fault_type值的含义的描述内容,可确认CPU异常触发重启具体为哪种死机问题。
表 1 CPU异常的fault_type含义
Store或Load异常¶
表 1 store或load异常关键信息
|
说明: 由于存储存在异步性,存储异常时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上进行处理,工具选取对应路径即可)。
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解析结果,如果没有显示结果,则参考“异常处理”的介绍进行处理。
分析解析结果。
打开memory_result.txt文件可以查询到当前的中断信息、任务信息、消息队列、信号量、全局变量、死机信息等。
异常处理¶
如果执行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的值。
上述两点都排查无误后,需要手工执行脚本进行问题定位。
方法如下:
























