//MAX_DB_NUM=127
这样就保证了循环变量i的值在正常范围内,从而避免了对指针pDBFat进行内存越界的操作。
从上面的测试过程中,我们可以看到:
如此严重的问题,仅仅是一个简单的错误引起的。
实际上,系统的不稳定往往是由这些看似很简单的小错误导致的。
这个问题给我们教训的是:
在直接对内存地址进行操作时,一定要保证其值的合法性,否则容易引起内存操作越界,给系统的稳定性带来潜在的威胁。
【案例1.2.4】
近日在CDB并行测试中发现一个问题:
我们需要的小区负荷话统结果总是为零,开始还以为小区负荷太小,于是加大短消息下发数量,但还为零,于是在程序中加入测试代码,把收到的数据在BAM上打印出来,
结果打印出来的数据正常,不可能为零,仔细查看相关代码,问题只可能在指针移位上有问题,果然在函数中发现一处比较隐蔽的错误。
/*功能:
一个BM模块内所有小区CDB侧广播消息忙闲情况*/
/*************************************************************/
voidCell_CBCH_Load_Static(structMsgCBFAR*pMsg)
{
。
。
。
memcpy((_UC*)&tmp_msg,pMsg,sizeof(tmp_msg));
pMsg=pMsg+sizeof(tmp_msg);//sizeof(tmp_msg)=10;本意是想移动10个字节,可是实际上指针移动了10*sizeof(structMsgCB)个字节;
CellNum=tmp_msg.usCellNum;
。
。
。
}
1
所以结构指针传入函数后,如要进行指针移动操作,最好先将其转化为_UC型再说。
总之指针操作要小心为上。
3、避免指针的非法引用
【案例1.3.1】
【正文】
在一次测试中,并没有记得做了什么操作,发现HONET系统的主机复位了,之后,系统又工作正常了。
由于没有打开后台的跟踪窗口,当时查了半天没有眉目。
过了半天,现象又出现了,而且这次是主机在反复复位,系统根本无法正常工作了。
我凭记忆,判断应该是与当时正在测试的DSL板的端口配置有关。
于是将板上所有端口配置为普通2B+D端口,重新加载在主机数据,现象消失。
于是初步定位为主机在DSL端口处理过程中有重大错误。
我在新的数据上努力恢复原出问题的现象,却一直没有重现,于是恢复原数据,加载后立即重现。
并注意到,当DSL端口激活时,主机复位。
仔细比较两种数据的差别,发现出现主机复位问题的数据中DSL板配置了MNT/MLT端口,但是没有做DSL端口之间的半永久数据。
于是在程序中不断加打印语句,通过后台的DBWIN调试程序跟踪,最后终于定位为:
每当执行到portdsl.c的DeviceDslMsgProc()函数中处理U口透传的
if(SPC_STATE_OK==pSpcCB->bySpcState)
语句时,主机复位。
但是该语句似乎并无不妥。
再分析整个函数,pSpcCB在函数前部分已经被赋值,
pSpcCB=SpcCB+(PortTable+index)->spcNo;
但由于得到index后,没有任何判断,导致若MNT/MLT端口没有做半永久,端口激活后,执行此部分函数,(PortTable+index)->spcNo有可能为NULL_WORD,于是,运算后,pSpcCB可能为非法值。
此时主机在取进行判断,就不知会导致什么后果了。
其实,改起来很简单,只要在这两句前增加一个判断就行了。
于是,修改代码为:
if((PortTable+index)->spcNo!
=NULL_WORD)
{
pSpcCB=SpcCB+(PortTable+index)->spcNo;
if(SPC_STATE_OK==pSpcCB->bySpcState)
{。
。
。
}
}
修改后,问题不再重现。
经过分析可以发现,编译环境是有很大的容许空间的,若主机没有做充分的保护,很可能会有极严重的随即故障出现。
所以编程时一定要考虑各种可能情况;而测试中遇到此类死机问题,则要耐心的定位到具体是执行哪句代码时出现的,再进行分析。
因为问题很隐蔽,直接分析海一样的代码是很难发现的。
4、变量类型定义错误
【案例1.4.1】
【正文】
在FRI板上建几条FRPVC,其DLCI类型分别为:
10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。
相应的DLCI值为:
16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。
对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。
此时第一个想法是:
在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:
(1)先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复;
(2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。
至此基本可以断定原因就是出在这里。
带着这个目的查看原代码,发现在以下代码中有问题:
int_GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID)
{DWORDtempDlci;
charszArg[80];
1charszLine[80];
IDLowPVCEP;
DWORDdwDlciVal[5][2]=
{{16,1007},{16,1007},{1024,64511},
{2048,129023},{131072,4194303}};
...
}
typedefstructtagFrPppIntIWF
{
...
WORDwHdlcPort;
WORDwHdlcDlci;
WORDwPeerHdlcDlci;
WORDwPeerOldAtmPort;
...
}SFrPppIntIWFData;
DWORDSaveFrNetIntIWFData(DWORD*pdwWritePoint)
{
BYTEbSlotID,bPeerSlotID;
DWORDdwCCID,dwPeerCCID;
WORDwHdlcPort,wAtmPort,wIci,wPeerIci,wPeerHdlcPort;
WORDwCount;
...
}
DWORDSaveFrNetExtIWFData(DWORD*pdwWritePoint)
{
BYTEbSlotID;
DWORDdwCCID,dwPeerCCID;
WORDwHdlcPort,wAtmPort,wIci;
WORDwCount;
...
unSevData.FrNetExtIWF[wCount].bSlotID=bSlotID;
unSevData.FrNetExtIWF[wCount].wHdlcPort=wHdlcPort;
unSevData.FrNetExtIWF[wCount].wHdlcDlci=gFrPVCEP[bSlotID][gFrPVCC[bSlotID][dwCCID].dwLoPVCEP].dwDLCI;
unSevData.FrNetExtIWF[wCount].wOldAtmPort=wAtmPort;
unSevData.FrNetExtIWF[wCount].wAtmDlci=gFrPVCEP[bSlotID][gFrPVCC[bSlotID][dwCCID].dwHiPVCEP].dwDLCI;
unSevData.FrNetExtIWF[wCount].dwMapMode=gFrPVCC[bSlotID][dwCCID].dwMapMode;
...
}
DWORDRestoreFrNetExtIWFData(WORDwSlotID,BYTE*pReadPoint)
{
WORDwCount,wTotalNetIWF;
BYTEbSlotID,bHdlcDlciType,bAtmDlciType;
WORDwOldAtmPort,wAtmDlci,wHdlcPort,wHdlcDlci;
DWORDdwMapMode,dwCIR,dwBe;
DWORDdwCCID,dwResult,dwAtmPort;
wTotalNetIWF=g_MuxData.SevDataSize.wFrNetExtIWFNum;
...
}
DWORDRestoreFrHdlcIntIWFData(WORDwSlotID,BYTE*pReadPoint)
{
WORDwCount,wTotalHdlcIWF;
DWORDdwCCID,dwPeerCCID,dwAtmPort,dwPeerAtmPort;
DWORDdwResult;
BYTEbSlotID,bPeerSlotID;
WORDwHdlcPort,wOldAtmPort,wCIR;
WORDwPeerHdlcPort,wPeerOldAtmPort;
...
}
其中涉及DLCI值的变量都为WORD(即无符号短整型)类型,在程序的处理时,出现WORD和DWORD(无符号长整型)类型在一句中同时存在的情况,至此可以判断问题出在这里。
由于DLCI值在不同类型时的取值范围不同,前三种类型的取值范围为16~991,第四种取值范围为2048~126975,第五种取值范围为131072~4194303,所以当采用前三种DLCI类型时,采用WORD类型最大值为65535,已经完全够用了;而对于第四种类型时,其取值在超过65535时,获取DLCI值的函数_GetFrDlci()采用DWORD类型,而负责保存和恢复的两个函数SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值当作WORD类型进行处理,因此导致DLCI取值越界,于是程序把原本为长整型的DLCI强制转换成整型,从而导致DLCI值在恢复时,比原数据小65536。
而在程序运行过程中,这些数据保存在DRAM中,程序运行直接从DRAM中获取数据,程序不会出错;当FRI板复位或插拔后,需要从FLASH中读取数据,此时恢复函数的错误就表现出来。
另一个问题是为什么23/4类型的DLCI数据不能恢复?
这是由于对于23/4类型的PVC,其DLCI的取值范围为:
131072~4194303,而程序强制转换并恢复的数据最大只能是65535,所以这条PVC不能恢复。
至此,DLCI数据恢复出错的原因完全找到,解决的方法是将DLCI的类型改为DWORD类型。
从这个案例可以看出,在程序开发中一个很低级的错误,将在实际工作中造成很严重的后果。
【案例1.4.2】
【正文】
在FRI板上建几条FRPVC,其DLCI类型分别为:
10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。
相应的DLCI值为:
16、234、991、126975、1234567,然后保存,重起MUX,观察PVC的恢复情况,结果DLCI值为16、234和991的PVC正确恢复,而DLCI=126975的PVC恢复的数据错误为61439,而DLCI=1234567的PVC完全没有恢复。
对于17/4类型,DLCI=126975的PVC在恢复时变成61439,根据这条线索,查找原因,发现126975-61439=65535,转化二进制就是10000000000000000,也就是说在数据恢复或保存时把原数据的第一个1给忽略了。
此时第一个想法是:
在程序处理中,把无符号长整型变量当作短整型变量处理了,为了证实这个判断,针对17bit/4bytes类型又重新设计测试用例:
(1)先建PVC,DLCI=65535,然后保存,重起MUX,观察PVC的恢复情况,发现PVC能够正确恢复;
(2)再建PVC,DLCI=65536,然后保存,重起MUX,观察PVC的恢复情况,此时PVC不能正确恢复。
至此基本可以断定原因就是出在这里。
带着这个目的查看原代码,发现在以下代码中有问题:
int_GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID)
{DWORDtempDlci;
cha