华为软件编程规范培训实例与练习.docx
- 文档编号:6670071
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:49
- 大小:136.03KB
华为软件编程规范培训实例与练习.docx
《华为软件编程规范培训实例与练习.docx》由会员分享,可在线阅读,更多相关《华为软件编程规范培训实例与练习.docx(49页珍藏版)》请在冰豆网上搜索。
华为软件编程规范培训实例与练习
软件编程规范培训实例与练习
•问题分类
1逻辑类问题(A类)-指设计、编码中出现的计算正确性和一致性、程序逻辑控制等方面出现的问题,在系统中起关键作用,将导致软件死机、功能正常实现等严重问题;
接口类问题(B类)-指设计、编码中出现的函数和环境、其他函数、全局/局部变量或数据变量之间的数据/控制传输不匹配的问题,在系统中起重要作用,将导致模块间配合失效等严重问题;
维护类问题(C类)-指设计、编码中出现的对软件系统的维护方便程度造成影响的问题,在系统中不起关键作用,但对系统后期维护造成不便或导致维护费用上升;
可测试性问题(D类)-指设计、编码中因考虑不周而导致后期系统可测试性差的问题。
•处罚办法
问题发生率:
P=D/S
D=DA+0.5DB+0.25DC
其中:
P-问题发生率
D-1个季度内错误总数
DA-1个季度内A类错误总数
DB-1个季度内B类错误总数
DC-1个季度内C类错误总数
S-1个季度内收到问题报告单总数
1)当D≥3时,如果P≥3%,将进行警告处理,并予以公告;
2)当D≥5时,如果P≥5%,将进行罚款处理,并予以公告。
一、逻辑类代码问题
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测试中,在内存数据库正常的情况下的各种数据库方面的操作都是正常的。
为了进行数据库异常测试,于是将数据库内容人为地破坏了。
发现在对数据库进行比较操作时,出现程序跑死了现象。
经过跟踪调试发现问题出现在如下一段代码中:
1for(i=0;i
2{
3pDBFat=(_NM_DBFAT_STRUC*)(NVDB_BASE+DBFAT_OFFSET+i*DBFAT_LEN);
4if(fat_check(pDBFat)!
=0)
5{
6pSysHead->system_flag=0;
7head_sum();
8continue;
9}
10if(strlen(dbf->dbf_name)!
=0&&strncmp(dbf->dbf_name,pDBFat->dbf_name,strlen(dbf->dbf_name))==0)
11{
12dbf_ptr1=(_UC*)pDBFat->dbf_head;
13filesize=pDBFat->dbf_fsize;
14break;
15}
16}
在测试时发现程序死在循环之中,得到的错误记录是"BusError"(总线出错),由此可以说明出现了内存操作异常。
经过跟踪变量值发现循环变量i的阀值pSysHead->dbf_count的数值为0xFFFFFFFF,该值是从被破坏的内存数据库中获取的,正常情况下该值小于127。
而pDBFat是数据库的起始地址,如果pSysHead->dbf_count值异常过大,将导致pDBFat值超过最大内存地址值,随后进行的内存操作将导致内存操作越界错误,因而在测试过程中数据库破坏后就出现了主机死机的现象。
上面的问题解决起来很容易,只需在第一行代码中增加一个判断条件即可,如下:
for(i=0;i //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; charszArg[80]; charszLine[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,bHdl
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 华为 软件 编程 规范 培训 实例 练习