1概述
在实际的很多情况下,都有直接访问物理内存的要求,例如在实时高速数据采集系统中,对I/O板上配置的存储器的访问。但是,为了保证系统的安全性和稳定性,操作系统并不提倡应用程序直接访问硬件资源。因此,随着操作系统的改进,以前在DOS下很容易实现的特定物理内存的读写操作,现在却变得相当困难。
在嵌入式VxWorks环境下,软件运行过程中如果需要观察某个内存区的数据结构,必须具备两个条件:首先,目标可执行文件在编译时需要附加调试信息;其次,必须运行Tornado调试工具或GDB调试工具。在可执行文件中附加调试信息会使得可执行文件的规模变得非常巨大,在存储容量有限的系统,特别是嵌入式系统中,往往是不允许的;另外,调试工具需要事先安装到位。因此,当目标系统规模很大时,启动调试模式的速度非常慢,往往导致目标系统或主机系统近似死机。
根据实际情况的需要,有必要提出一种内存数据解析器,可以在VxWorks环境下脱离调试工具的支持,随时观察内存数据结构,而且不需要在可执行文件中添加调试信息。
2数据解析器功能分析
内存数据解析器由两部分组成:一部分叫做生成器,另一部分叫做解析器。具体工作过程如图1所示。
图1内存数据解析器工作过程
以C语言编写的程序为例说明。首先,在用C语言编译器(比如GNU编译器)对目标C原始文件(即.C和H文件)进行编译预处理后,生成编译预处理文件。然后,用生成器对编译预处理文件进行处理后,生成器生成解析器的C文件,并把它加入到目标工程中,和目标C原始文件一起进行编译、链接,得到带解析器的可执行文件。
3生成器实现
生成器的功能是扫描预编译结果文件中的所有数据类型定义,针对这些数据类型定义生成其相应的解析器C文件。它的工作工作原理与编译器类似,其工作步骤包括词法分析、语法分析、生成解析器代码。
3.1词法分析
词法分析首先确定合法字符,辨识所有C语言中的合法字符;接着确定关键字,如struct、union、enum、int、long、short、char、signed、unsigned、typedef等;然后确定分隔符,如{、}、(、)、[、]、;、+、-、*、/等,辨识数字和字符串。词法分析的结果是一系列条目,也就是一系列条目组成的流将作为语法分析的输入。上述各种关键字、分隔符、数字、字符串都表示为一个条目,分别有不同的名称。数字、字符串条目还会带参数,表示数字或字符串的内容。
3.2语法分析
语法分析首先确定各种类型定义的语法表示,用巴克斯范式进行规范。跳过函数定义、变量定义,只对类型定义进行语法分析。用状态机方法识别类型定义的种类包括结构、联合、枚举等。语法分析要能够识别嵌套类型定义。每识别出一个类型定义,就要针对这些定义生成解析器C代码。
3.3生成解析器代码
首先要确定各种数据类型展现的格式,也就是希望以怎样的格式打印各种数据类型的内容。解析器C代码由一系列打印函数组成,每个数据类型定义都对应一个打印函数,每个打印函数都有几个输入参数:变量的内存地址、希望打印的嵌套层级、希望打印的数组元素个数、变量名称字符串等。
4解析器C文件
解析器可以分为固有数据类型和非固有数据类型两种情况。要提供固有数据类型int、char、short、long等的打印函数作为解析器C代码的一部分。各种数据类型定义的打印函数由一系列变量都对应一个打印函数。固有数据类型用固有数据类型打印函数打印,非固有数据类型用非固有数据类型的打印函数打印。
4.1固有数据类型
对于C语言固有的数据类型,比如long、int、char、short等,只需类似如下的处理就可以实现:首先检查参数并调整,判断是否是数组,若是则循环输出数组数值,否则直接输出数据数值;接着打印字符类型,判断数据是否全部输出,若是,打印数据内存地址及大小。这样,只需调用相应的打印函数,就能够对这片内存进行观察。固有数据类型解析器处理流程如图2所示。
图2固有数据类型解析器处理流程
对于单字符型数据,解析打印结果的格式如下:
变量名数值类型地址尺寸
cTextFlag=17=0x11fTTT_INT80x00e5fe4d,size:1
对于字符型数组,解析打印结构的格式将如下:
acMOI[0_8]=24=0x18fTTT_INT80x00e5fe54,size:1
acMOI[1_8]=25=0x19fTTT_INT80x00e5fe55,size:1
acMOI[2_8]=26=0x1afTTT_INT80x00e5fe56,size:1
acMOI[3_8]=27=0x1bfTTT_INT80x00e5fe57,size:1
acMOI[4_8]=28=0x1cfTTT_INT80x00e5fe58,size:1
acMOI[5_8]=29=0x1dfTTT_INT80x00e5fe59,size:1
acMOI[6_8]=30=0x1efTTT_INT80x00e5fe5a,size:1
acMOI[7_8]=31=0x1ffTTT_INT80x00e5fe5b,size:1
4.2非固有数据类型
对于如下的结构类型定义:
typedef struct rdbs_n7_getadjofficeinfo_msg_rsp{
UINT32 ulResult;
UINT16 usOfficeId;
UINT16 usOfficeNo;
RDBS_SPC_T stDpc;
UINT8 ucOfficeType;
UINT8 ucNetType;
UINT8 ucSsf;
UINT8 ucSpType;
UINT8 ucConnectType;
UINT8 ucTestFlag;
UINT8 ucMtpType;
UINT8 ucSpcType;
RDBS_MO_IDT stRdbsMoId;
}RDBS_N7_GETADJOFFICEINFO_MSG_RSP_T
在对参数进行检查及调整后,根据数组元素个数循环执行下面代码:
if(NULL==vpcOutTypeName)
vpcOutTypeName="rdbs_n7_getadjofficeinfo_msg_rsp";
fZZZ_head(pBuff,vuiMaxGrade,vuiNumb,vpcVarName,vuiGrade,uiIndex,sizeof(struct rdbs_n7_getadjofficeinfo_msg_rsp),vpcOutTypeName);
if(vuiGrade+1!=vuiMaxGrade){
fTTT_UINT32(&pBuff>ulResult,vuiMaxGrade,1,
"ulResult",vuiGrade+1,NULL);
fTTT_UINT16(&pBuff>usOfficeId,uviMaxGrade,1,
"usOfficeId",vuiGrade+1,NULL);
fTTT_UINT16(&pBuff>usOfficeId,uviMaxGrade,1,
"usOfficeId",vuiGrade+1,NULL);
fTTT_RDBS_SPC_T(&pBuff>stDpc,vuiMaxGrade,1,
"stDpc",vuiGrad+1,NULL);
fTTT_UINT8(&pBuff>ucOfficeType,vuiMaxGrade,1,
"ucOfficeType",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucNetType,vuiMaxGrade,1,
"ucNetType",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucSsf,vuiMaxGrade,1,
"ucSsf",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucSpType,vuiMaxGrade,1,
"ucSpType",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucConnectType,vuiMaxGrade,1,
"ucConnectType",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucTestFlag,vuiMaxGrade,1,
"ucTestFlag",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucMtpType,vuiMaxGrade,1,
"ucMtpType",vuiGrade+1,NULL);
fTTT_UINT8(&pBuff>ucSpcType,vuiMaxGrade,1,
"ucSpcType",vuiGrade+1,NULL);
fTTT_RDBS_MO_ID_T(&pBuff>stRdbsMoId,
vuiMaxGrade,1,"stRdbsMoId",vuiGrad+1,NULL);
pBuff++;
uiIndex++;
}
将生成如下的解析器C文件:
This fTTT_RDBS_N7_GETADJOFFICEINFO_MSG_RSP_T 0x00e5fe3c, size:32
ulResult=50462976=0x3020100 fTTT_UINT32 0x00e5fe3c,size:4
usOfficeId=1284=0x504 fTTT_UINT16 0x00e5fe40,size:2
usOfficeNo=1798=0x706 fTTT_UINT16 0x00e5fe42,size:2
stDpc fTTT_RDBS_SPC_T 0x005fe44, size:4
aucSpc fTTT_SPC 0x00e5fe44,size:3
fTTT_UINT8[0_3]=8=0x8 fTTT_UINT8 0x00e5fe44,size:1
fTTT_UINT8[1_3]=8=0x9 fTTT_UINT8 0x00e5fe45,size:1
fTTT_UINT8[2_3]=8=0xa fTTT_UINT8 0x00e5fe46,size:1
aucRsv=11=0xb fTTT_UINT8 0x00e5fe47,size:1
ucOfficeType=12=0xc fTTT_UINT8 0x00e5fe48,size:1
ucNetType=13=0xd fTTT_UINT8 0x00e5fe49,size:1
ucSsf=14=0xe fTTT_UINT8 0x00e5fe4a,size:1
ucSpType=15=0xf fTTT_UINT8 0x00e5fe4b,size:1
ucConnectType=16=0x10 fTTT_UINT8 0x00e5fe4c,size:1
ucTestFlag=17=0x11 fTTT_UINT8 0x00e5fe4d,size:1
ucMtpType=18=0x12 fTTT_UINT8 0x00e5fe4e,size:1
ucSpcType=19=0x13 fTTT_UINT8 0x00e5fe4f,size:1
stRdbMoId fTTT_RDBS_MO_ID_T 0x00e5fe50,size:12
uIMOC=387323156=0x17161514 fTTT_UINT32
0x00e5fe50,size:4
aucMOI[0_8]=24=0x18 fTTT_UINT8 0x00e5fe54,size:1
aucMOI[1_8]=24=0x19 fTTT_UINT8 0x00e5fe55,size:1
aucMOI[2_8]=24=0x1a fTTT_UINT8 0x00e5fe56,size:1
aucMOI[3_8]=24=0x1b fTTT_UINT8 0x00e5fe57,size:1
aucMOI[4_8]=24=0x1c fTTT_UINT8 0x00e5fe58,size:1
aucMOI[5_8]=24=0x1d fTTT_UINT8 0x00e5fe59,size:1
aucMOI[6_8]=24=0x1e fTTT_UINT8 0x00e5fe5a,size:1
aucMOI[7_8]=24=0x1f fTTT_UINT8 0x00e5fe5b,size:1
在C程序中还有其他数据类型,比如联合、枚举等,其解析器C文件的构成方法与上述方法类似,不再一一列举。
5总结
按照本文描述的方法实现内存数据解析器,在VxWorks环境下对内存数据进行观察已不再是一件非常艰难的事情。只需要调用相应的打印函数,就能够以数据结构的方式直观地对这片内存进行观察,极大地提高了代码的调试效率。