文档库 最新最全的文档下载
当前位置:文档库 › 软件编程规范培训实例与练习(华为)

软件编程规范培训实例与练习(华为)

软件编程规范培训实例与练习

?问题分类

1 逻辑类问题(A类)-指设计、编码中出现的计算正确性和一致性、程序逻辑控制等方面出现的问题,在系统中起关键作用,将导致软件死机、功能正常实现等严重问题;

接口类问题(B类)-指设计、编码中出现的函数和环境、其他函数、全局/局部变量或数据变量之间的数据/控制传输不匹配的问题,在系统中起重要作用,将导致模块间配合失效等严重问题;

维护类问题(C类)-指设计、编码中出现的对软件系统的维护方便程度造成影响的问题,在系统中不起关键作用,但对系统后期维护造成不便或导致维护费用上升;

可测试性问题(D类)-指设计、编码中因考虑不周而导致后期系统可测试性差的问题。

?处罚办法

问题发生率:

P=D/S

D=D A+0.5D B+0.25D C

其中:

P -问题发生率

D-1个季度内错误总数

D A -1个季度内A类错误总数

D B -1个季度内B类错误总数

D C -1个季度内C类错误总数

S-1个季度内收到问题报告单总数

1)当D≥3时,如果P≥3%,将进行警告处理,并予以公告;

2)当D≥5时,如果P≥5%,将进行罚款处理,并予以公告。

目录

第5页

一、逻辑类代码问题

1、变量/指针在使用前就必须初始化第5页

第5页【案例1.1.1】

2、防止指针/数组操作越界第5页

第5页【案例1.2.1】

第6页【案例1.2.2】

第7页【案例1.2.3】

第8页【案例1.2.4】

3、避免指针的非法引用第9页

第9页【案例1.3.1】

4、变量类型定义错误第10页

第10页【案例1.4.1】

5、正确使用逻辑与&&、屏蔽&操作符第17页

第17页【案例1.5.1】

6、注意数据类型的匹配第18页

第18页【案例1.6.1】

第18页【案例1.6.2】

7、用于控制条件转移的表达式及取值范围是否书写正确第20页

第20页【案例1.7.1】

第21页【案例1.7.2】

第22页【案例1.7.3】

8、条件分支处理是否有遗漏第24页

第24页【案例1.8.1】

9、引用已释放的资源第26页

第26页【案例1.9.1】

10、分配资源是否已正确释放第28页

第28页【案例1.10.1】

第29页【案例1.10.2】

第30页【案例1.10.3】

第33页【案例1.10.5】

第35页【案例1.10.6】

第38页【案例1.10.7】

11、防止资源的重复释放第39页

第39页【案例1.11.1】

12、公共资源的互斥性和竞用性第40页

第40页【案例1.12.1】

第40页【案例1.12.2】

第43页

二、接口类代码问题

1、对函数参数进行有效性检查第43页

第43页【案例2.1.1】

第43页【案例2.1.2】

第44页【案例2.1.3】

第46页【案例2.1.4】

第47页【案例2.1.5】

第48页【案例2.1.6】

2、注意多出口函数的处理第49页

第49页【案例2.2.1】

第51页

三、维护类代码问题

1、统一枚举类型的使用第51页

第51页【案例3.1.1】

2、注释量至少占代码总量的20%第51页

第51页【案例3.2.1】对XXX产品BAM某版本部分代码注释量的统计

第52页

四、产品兼容性问题

1、系统配置、命令方式第52页

第52页【案例4.1.1】

第53页【案例4.1.2】

2、设备对接第54页

3、其他第55页

第55页【案例4.3.1】

第58页

五、版本控制问题

1、新老代码中同一全局变量不一致第58页

第58页【案例5.1.1】

第59页

六、可测试性代码问题

1、调试信息/打印信息的正确性第59页

第59页【案例6.1.1】

一、逻辑类代码问题

1、变量/指针在使用前就必须初始化

【案例1.1.1】

C语言中最大的特色就是指针。指针的使用具有很强的技巧性和灵活性,但同时也带来了很大的危险性。在XXX的代码中有如下一端对指针的灵活使用:... ...

_UC *puc_card_config_tab;

... ...

Get_Config_Table( AMP_CPM_CARD_CONFIG_TABLE,

&ul_card_config_num,

&puc_card_config_tab,

use_which_data_area

);

... ...

b_middle_data_ok = generate_trans_middle_data_from_original_data(

puc_card_config_tab,

Ul_card_config_num)

.... ...

其中红色部分巧妙的利用指向指针的指针为指针puc_card_config_tab赋值,而在兰色部分使用该指针。但在Get_Config_Table函数中有可能失败返回而不给该指针赋值。因此,以后使用的可能是一个非法指针。

指针的使用是非常灵活的,同时也存在危险性,必须小心使用。指针使用的危险性举世共知。在新的编程思想中,指针基本上被禁止使用(JAVA中就是这样),至少也是被限制使用。而在我们交换机的程序中大量使用指针,并且有增无减。

2、防止指针/数组操作越界

【案例1.2.1】

在香港项目测试中,发现ISDN话机拨新业务号码时,若一位一位的拨至18位,不会有问题。但若先拨完号码再成组发送,会导致MPU死机。

处理过程:

查错过程很简单,按呼叫处理的过程检查代码,发现某一处的判断有误,本应为小于18的判断,写成了小于等于18。

结论:

代码编写有误。

思考与启示:

1、极限测试必须注意,测试前应对某项设计的极限做好充分测试规划。

2、测试极限时还要注意多种业务接入点,本例为ISDN。对于交换机来说,任何一种业务都要分别在模拟话机、ISDN话机、V5话机、多种形式的话务台上做测试。对于中继的业务,则要充分考虑各种信令:TUP、ISUP、PRA、NO1、V5等等。

【案例1.2.2】

对某交换类进行计费测试,字冠011对应1号路由、1号子路由,有4个中继群11,12,13,14(都属于1#模块),前后两个群分别构成自环。其中11,13群向为出中继,12,14群向为入中继,对这四个群分别进行计费设置,对出入中继都计费。电话60640001拨打01160010001两次,使四个群都有机会被计费,取话单后浏览话单发现对11群计费计次表话单出中继群号不正确,其它群的计次表中出中继群号正常。

处理过程:

与开发人员在测试组环境多次重复以上步骤,发现11群的计次表话单有时正

常,有时其出中继群号就为一个随机值,发生异常的频率比较高。为什么其它群的话单正常,唯独11群不正常呢?11群是四个群中最小的群,其中继计次表位于缓冲区的首位,打完电话后查询内存发现出中继群号在内存中是正确的,取完话单后再查就不正确了。

结论:

话单池的一个备份指针Pool_head_1和中继计次表的头指针重合,影响到第一个中继计次表的计费。

思考与启示:

随机值的背后往往隐藏着指针问题,两块内存缓冲区的交界处比较容易出现问题,在编程时是应该注意的地方。

【案例1.2.3】

【正文】

在接入网产品A测试中,在内存数据库正常的情况下的各种数据库方面的操作都是正常的。为了进行数据库异常测试,于是将数据库内容人为地破坏了。发现在对数据库进行比较操作时,出现程序跑死了现象。

经过跟踪调试发现问题出现在如下一段代码中:

1 for(i=0; idbf_count; i++)

2 {

3 pDBFat = (_NM_DBFAT_STRUC *)(NVDB_BASE +

DBFAT_OFFSET + i*DBFAT_LEN);

4 if(fat_check(pDBFat) != 0)

5 {

6 pSysHead->system_flag = 0;

7 head_sum();

8 continue;

9 }

10 if(strlen(dbf->dbf_name) != 0 && strncmp(dbf->dbf_name, pDBFat->dbf_name, strlen(dbf->dbf_name)) == 0)

11 {

12 dbf_ptr1 = (_UC *)pDBFat->dbf_head;

13 filesize = pDBFat->dbf_fsize;

14 break;

15 }

16 }

在测试时发现程序死在循环之中,得到的错误记录是"Bus Error"(总线出错),由此可以说明出现了内存操作异常。

经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为

0xFFFFFFFF,该值是从被破坏的内存数据库中获取的,正常情况下该值小于127。而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将导致pDBFat值超过最大内存地址值,随后进行的内存操作将导致内存操作越界错误,因而在测试过程中数据库破坏后就出现了主机死机的现象。

上面的问题解决起来很容易,只需在第一行代码中增加一个判断条件即可,如下:

for(i=0; idbf_coun && i < MAX_DB_NUM; i++)

// MAX_DB_NUM=127

这样就保证了循环变量i的值在正常范围内,从而避免了对指针pDBFat进行内存越界的操作。

从上面的测试过程中,我们可以看到:如此严重的问题,仅仅是一个简单的错误引起的。实际上,系统的不稳定往往是由这些看似很简单的小错误导致的。这个问题给我们教训的是:在直接对内存地址进行操作时,一定要保证其值的合法性,否则容易引起内存操作越界,给系统的稳定性带来潜在的威胁。

【案例1.2.4】

近日在CDB并行测试中发现一个问题:我们需要的小区负荷话统结果总是为零,开始还以为小区负荷太小,于是加大短消息下发数量,但还为零,于是在程序中加入测试代码,把收到的数据在BAM上打印出来,

结果打印出来的数据正常,不可能为零,仔细查看相关代码,问题只可能在指针移位上有问题,果然在函数中发现一处比较隐蔽的错误。

/* 功能:一个BM模块内所有小区CDB侧广播消息忙闲情况*/

/*************************************************************/

void Cell_CBCH_Load_Static(struct MsgCB FAR *pMsg)

{

。。。

memcpy((_UC *)&tmp_msg,pMsg,sizeof(tmp_msg));

pMsg=pMsg+sizeof(tmp_msg);//sizeof(tmp_msg)=10;本意是想移动10个字节,可是实际上指针移动了10*sizeof(struct MsgCB)个字节;

CellNum=tmp_https://www.wendangku.net/doc/f816945887.html,CellNum;

。。。

}

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, DWORD dwDlciType, DWORD dwPortType, DWORD dwSlotID, DWORD dwPortID)

{ DWORD tempDlci;

charszArg[80];

1 charszLine[80];

ID LowPVCEP;

DWORD dwDlciVal[5][2] =

{ {16,1007}, {16,1007}, {1024,64511},

{2048,129023}, {131072,4194303} } ;

...

}

typedef struct tagFrPppIntIWF

{

...

WORD wHdlcPort;

WORD wHdlcDlci;

WORD wPeerHdlcDlci;

WORD wPeerOldAtmPort;

...

} SFrPppIntIWFData;

DWORD SaveFrNetIntIWFData ( DWORD *pdwWritePoint ) {

BYTE bSlotID, bPeerSlotID;

DWORD dwCCID, dwPeerCCID;

WORD wHdlcPort, wAtmPort, wIci, wPeerIci, wPeerHdlcPort ;

WORD wCount;

...

}

DWORD SaveFrNetExtIWFData ( DWORD *pdwWritePoint ) {

BYTE bSlotID;

DWORD dwCCID, dwPeerCCID;

WORD wHdlcPort, wAtmPort, wIci ;

WORD wCount;

...

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;

...

}

DWORD RestoreFrNetExtIWFData ( WORD wSlotID, BYTE *pReadPoint ) {

WORD wCount, wTotalNetIWF;

BYTE bSlotID, bHdlcDlciType, bAtmDlciType;

WORD wOldAtmPort, wAtmDlci, wHdlcPort, wHdlcDlci;

DWORD dwMapMode, dwCIR, dwBe;

DWORD dwCCID, dwResult, dwAtmPort;

wTotalNetIWF = g_MuxData.SevDataSize.wFrNetExtIWFNum;

...

}

DWORD RestoreFrHdlcIntIWFData ( WORD wSlotID, BYTE *pReadPoint ) {

WORD wCount, wTotalHdlcIWF;

DWORD dwCCID, dwPeerCCID, dwAtmPort, dwPeerAtmPort;

DWORD dwResult;

BYTE bSlotID, bPeerSlotID;

WORD wHdlcPort, wOldAtmPort, wCIR;

WORD wPeerHdlcPort, 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, DWORD dwDlciType, DWORD dwPortType, DWORD dwSlotID, DWORD dwPortID)

{ DWORD tempDlci;

charszArg[80];

charszLine[80];

ID LowPVCEP;

DWORD dwDlciVal[5][2] =

{ {16,1007}, {16,1007}, {1024,64511},

{2048,129023}, {131072,4194303} } ;

...

}

typedef struct tagFrPppIntIWF

{

...

WORD wHdlcPort;

WORD wHdlcDlci;

WORD wPeerHdlcDlci;

WORD wPeerOldAtmPort;

...

} SFrPppIntIWFData;

DWORD SaveFrNetIntIWFData ( DWORD *pdwWritePoint )

{

BYTE bSlotID, bPeerSlotID;

DWORD dwCCID, dwPeerCCID;

WORD wHdlcPort, wAtmPort, wIci, wPeerIci, wPeerHdlcPort ;

WORD wCount;

...

}

DWORD SaveFrNetExtIWFData ( DWORD *pdwWritePoint )

{

BYTE bSlotID;

DWORD dwCCID, dwPeerCCID;

WORD wHdlcPort, wAtmPort, wIci ;

WORD wCount;

...

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;

...

}

DWORD RestoreFrNetExtIWFData ( WORD wSlotID, BYTE *pReadPoint )

{

WORD wCount, wTotalNetIWF;

BYTE bSlotID, bHdlcDlciType, bAtmDlciType;

WORD wOldAtmPort, wAtmDlci, wHdlcPort, wHdlcDlci;

DWORD dwMapMode, dwCIR, dwBe;

DWORD dwCCID, dwResult, dwAtmPort;

wTotalNetIWF = g_MuxData.SevDataSize.wFrNetExtIWFNum;

...

}

DWORD RestoreFrHdlcIntIWFData ( WORD wSlotID, BYTE *pReadPoint )

{

WORD wCount, wTotalHdlcIWF;

DWORD dwCCID, dwPeerCCID, dwAtmPort, dwPeerAtmPort;

DWORD dwResult;

BYTE bSlotID, bPeerSlotID;

WORD wHdlcPort, wOldAtmPort, wCIR;

WORD wPeerHdlcPort, 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类型。从这个案例可以看出,在程序开发中一个很低级的错误,将在实际工作中造成很严重的后果。

5、正确使用逻辑与&&、屏蔽&操作符

【案例1.5.1】

【案例描述】:由于C语言中位与比求模效率高,因而系统设计时,对于模128的地方都改为与127,系统定义的宏为#define MOD128 127和#define W_MOD 127(定义的宏的名字易引起误解),但实际程序中还是采取求模,从而引起发送窗口欲重发的和实际重发的不一致,最终导致链路复位此类严重问题,曾在定位此问题时花了不少时间。

【处理过程】:处理过程如下:

#define MOD128 127 //队列长128,当队头到128时,上其返回。#define W_MOD 127 //发送窗口队列,意义同上。

在函数L2_TO_L1()中,有如下语句:

linkstate_ptr->SendWin.head = (head + 1) % W_MOD ;

这里当head=126时,SendWin.head = 0,这将造成发送窗口指针和队列窗口指针错位,造成链路复位;

另外,在重发函数void INVOKE_RETRANSMISSION(_US logic_link,_US n_r)中,有如下语句:

retran_num = (LinkState[logic_link].Vs + MOD128 - (_UC)n_r) %

MOD128 ;

w_head = (LinkState[logic_link].SendWin.head + W_MOD -

retran_num) % W_MOD ;

第一个语句求欲重发的消息包个数,第二个语句求重发的起始位置,当Vs小于

n_r时,将造成实际重发数小于欲重发数,同时造成实际起始重发位置和欲重发起始位置错开,从而引起链路复位。上面三个语句应该做如下改动:

linkstate_ptr->SendWin.head = (head + 1) & W_MOD ;

retran_num = (LinkState[logic_link].Vs + MOD128 + 1 - (_UC)n_r) & MOD128 ;

w_head = (LinkState[logic_link].SendWin.head + W_MOD + 1 -

retran_num) & W_MOD ;

【结论】:由于链路通信对系统效率要求很高,算法采用效率最高的,但位与(&)和求模(%)这小小的区别,造成的竟是链路复位这种严重的错误。

【思考与启示】:对这类问题,大家在阅读代码或代码审查时一定要注意,仔细一点往往能发现问题,但在测试中来定位这种问题,花费的时间往往更长。

6、注意数据类型的匹配

【案例1.6.1】

【案例描述】

下面通过测试中的一个例子来说明这个问题:命令DSP N7C是用来显示NO7

电路状态的,其参数设备类型DID支持TUP和ISUP,参数信道号BSN支持多值输入(最多支持32路查询),正常情况下该命令没有问题。但试了非正常情况下,问题就出来了。

1、首先试BSN参数越界情况,即参数BSN超过32路查询,选了几个数据段,问题就出来了。对于0&&300和0&&256,该命令返回结果不一致,对前者认为参数越界,对后者返回执行成功。

2、对于参数DID,选定一种设备类型(TUP或ISUP),让参数BSN所包含的32路电路跨越TUP和ISUP,两次结果是不一致的。

【处理过程】

反馈到开发人员那里,第一个问题是BAM的问题,第二个问题是SM的问题。【结论】

1、为数据超出范围溢出造成,int值赋值给BYTE,造成数据丢失。

2、问题的产生是因为查询的第一个信道是TUP电路,但是却按ISUP电路查询。ISUP的维护处理函数判断第一个信道不是ISUP信道,认为整个的PCM不是ISUP类型的PCM,返回全部的电路状态为未安装。消息处理不合理。TUP也会产生如此错误。

【思考与启示】

我们的MML命令并不是无懈可击的,许多表面上的小问题,往往隐藏着代码的缺陷和错误。

【案例1.6.2】

【正文】

当我们使用PC-LINT检查代码时,会发现大量的数据类型不匹配的告警,大部分情况下,这种代码上存在的问题并不会引起程序功能实现上的错误,但有些情况下,也许会产生严重的问题:

一、不同数据类型变量之间赋值引起的问题,实际上,该类问题也可以分为几种情况:

相关文档