軟件編程規(guī)范培訓實例與練習_第1頁
軟件編程規(guī)范培訓實例與練習_第2頁
軟件編程規(guī)范培訓實例與練習_第3頁
軟件編程規(guī)范培訓實例與練習_第4頁
軟件編程規(guī)范培訓實例與練習_第5頁
已閱讀5頁,還剩54頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

軟件編程規(guī)范培訓實例與練習問題分類1邏輯類問題(A類)-指設(shè)計、編碼中出現(xiàn)的計算正確性和一致性、程序邏輯控制等方面出現(xiàn)的問題,在系統(tǒng)中起關(guān)鍵作用,將導致軟件死機、功能正常實現(xiàn)等嚴重問題;接口類問題(B類)-指設(shè)計、編碼中出現(xiàn)的函數(shù)和環(huán)境、其他函數(shù)、全局/局部變量或數(shù)據(jù)變量之間的數(shù)據(jù)/控制傳輸不匹配的問題,在系統(tǒng)中起重要作用,將導致模塊間配合失效等嚴重問題;維護類問題(C類)-指設(shè)計、編碼中出現(xiàn)的對軟件系統(tǒng)的維護方便程度造成影響的問題,在系統(tǒng)中不起關(guān)鍵作用,但對系統(tǒng)后期維護造成不便或?qū)е戮S護費用上升;可測試性問題(D類)-指設(shè)計、編碼中因考慮不周而導致后期系統(tǒng)可測試性差的問題。處罰辦法問題發(fā)生率:P=D/SD=DA+0.5DB+0.25DC其中:P-問題發(fā)生率D-1個季度內(nèi)錯誤總數(shù)DA-1個季度內(nèi)A類錯誤總數(shù)DB-1個季度內(nèi)B類錯誤總數(shù)DC-1個季度內(nèi)C類錯誤總數(shù)S-1個季度內(nèi)收到問題報告單總數(shù)1)當D≥3時,如果P≥3%,將進行警告處理,并予以公告;2)當D≥5時,如果P≥5%,將進行罰款處理,并予以公告。目錄一、邏輯類代碼問題第5頁1、變量/指針在使用前就必須初始化第5頁【案例】第5頁2、防止指針/數(shù)組操作越界第5頁【案例】第5頁【案例】第6頁【案例】第7頁【案例】第8頁3、避免指針的非法引用第9頁【案例】第9頁4、變量類型定義錯誤第10頁【案例】第10頁5、正確使用邏輯與&&、屏蔽&操作符第17頁【案例】第17頁6、注意數(shù)據(jù)類型的匹配第18頁【案例】第18頁【案例】第18頁7、用于控制條件轉(zhuǎn)移的表達式及取值范圍是否書寫正確第20頁【案例】第20頁【案例】第21頁【案例1.7.3】第22頁8、條件分支處理是否有遺漏第24頁【案例】第24頁9、引用已釋放的資源第26頁【案例】第26頁10、分配資源是否已正確釋放第28頁【案例】第28頁【案例】第29頁【案例】第30頁【案例】第32頁【案例】第33頁【案例】第35頁【案例】第38頁11、防止資源的重復釋放第39頁【案例】第39頁12、公共資源的互斥性和競用性第40頁【案例】第40頁【案例】第40頁二、接口類代碼問題第43頁1、對函數(shù)參數(shù)進行有效性檢查第43頁【案例】第43頁【案例】第43頁【案例】第44頁【案例】第46頁【案例】第47頁【案例】第48頁2、注意多出口函數(shù)的處理第49頁【案例】第49頁三、維護類代碼問題第51頁1、統(tǒng)一枚舉類型的使用第51頁【案例】第51頁2、注釋量至少占代碼總量的20%第51頁【案例】對XXX產(chǎn)品BAM某版本部分代碼注釋量的統(tǒng)計第51頁四、產(chǎn)品兼容性問題第52頁1、系統(tǒng)配置、命令方式第52頁【案例】第52頁【案例】第53頁2、設(shè)備對接第54頁【案例】第54頁3、其他第55頁【案例】第55頁五、版本控制問題第58頁1、新老代碼中同一全局變量不一致第58頁【案例】第58頁六、可測試性代碼問題第59頁1、調(diào)試信息/打印信息的正確性第59頁【案例】第59頁一、邏輯類代碼問題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函數(shù)中有可能失敗返回而不給該指針賦值。因此,以后使用的可能是一個非法指針。指針的使用是非常靈活的,同時也存在危險性,必須小心使用。指針使用的危險性舉世共知。在新的編程思想中,指針基本上被禁止使用(JAVA中就是這樣),至少也是被限制使用。而在我們交換機的程序中大量使用指針,并且有增無減。2、防止指針/數(shù)組操作越界【案例】在香港項目測試中,發(fā)現(xiàn)ISDN話機撥新業(yè)務(wù)號碼時,若一位一位的撥至18位,不會有問題。但若先撥完號碼再成組發(fā)送,會導致MPU死機。處理過程:查錯過程很簡單,按呼叫處理的過程檢查代碼,發(fā)現(xiàn)某一處的判斷有誤,本應(yīng)為小于18的判斷,寫成了小于等于18。結(jié)論:代碼編寫有誤。思考與啟示: 1、極限測試必須注意,測試前應(yīng)對某項設(shè)計的極限做好充分測試規(guī)劃。2、測試極限時還要注意多種業(yè)務(wù)接入點,本例為ISDN。對于交換機來說,任何一種業(yè)務(wù)都要分別在模擬話機、ISDN話機、V5話機、多種形式的話務(wù)臺上做測試。對于中繼的業(yè)務(wù),則要充分考慮各種信令:TUP、ISUP、PRA、NO1、V5等等。【案例】對某交換類進行計費測試,字冠011對應(yīng)1號路由、1號子路由,有4個中繼群11,12,13,14(都屬于1#模塊),前后兩個群分別構(gòu)成自環(huán)。其中11,13群向為出中繼,12,14群向為入中繼,對這四個群分別進行計費設(shè)置,對出入中繼都計費。60640001撥打兩次,使四個群都有機會被計費,取話單后瀏覽話單發(fā)現(xiàn)對11群計費計次表話單出中繼群號不正確,其它群的計次表中出中繼群號正常。處理過程:與開發(fā)人員在測試組環(huán)境多次重復以上步驟,發(fā)現(xiàn)11群的計次表話單有時正常,有時其出中繼群號就為一個隨機值,發(fā)生異常的頻率比較高。為什么其它群的話單正常,唯獨11群不正常呢?11群是四個群中最小的群,其中繼計次表位于緩沖區(qū)的首位,打完后查詢內(nèi)存發(fā)現(xiàn)出中繼群號在內(nèi)存中是正確的,取完話單后再查就不正確了。結(jié)論:話單池的一個備份指針Pool_head_1和中繼計次表的頭指針重合,影響到第一個中繼計次表的計費。思考與啟示:隨機值的背后往往隱藏著指針問題,兩塊內(nèi)存緩沖區(qū)的交界處比較容易出現(xiàn)問題,在編程時是應(yīng)該注意的地方?!景咐俊菊摹吭诮尤刖W(wǎng)產(chǎn)品A測試中,在內(nèi)存數(shù)據(jù)庫正常的情況下的各種數(shù)據(jù)庫方面的操作都是正常的。為了進行數(shù)據(jù)庫異常測試,于是將數(shù)據(jù)庫內(nèi)容人為地破壞了。發(fā)現(xiàn)在對數(shù)據(jù)庫進行比較操作時,出現(xiàn)程序跑死了現(xiàn)象。經(jīng)過跟蹤調(diào)試發(fā)現(xiàn)問題出現(xiàn)在如下一段代碼中:1 for(i=0;i<pSysHead->dbf_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 }在測試時發(fā)現(xiàn)程序死在循環(huán)之中,得到的錯誤記錄是"BusError"(總線出錯),由此可以說明出現(xiàn)了內(nèi)存操作異常。經(jīng)過跟蹤變量值發(fā)現(xiàn)循環(huán)變量i的閥值pSysHead->dbf_count的數(shù)值為0xFFFFFFFF,該值是從被破壞的內(nèi)存數(shù)據(jù)庫中獲取的,正常情況下該值小于127。而pDBFat是數(shù)據(jù)庫的起始地址,如果pSysHead->dbf_count值異常過大,將導致pDBFat值超過最大內(nèi)存地址值,隨后進行的內(nèi)存操作將導致內(nèi)存操作越界錯誤,因而在測試過程中數(shù)據(jù)庫破壞后就出現(xiàn)了主機死機的現(xiàn)象。上面的問題解決起來很容易,只需在第一行代碼中增加一個判斷條件即可,如下:for(i=0;i<pSysHead->dbf_coun&&i<MAX_DB_NUM;i++)//MAX_DB_NUM=127這樣就保證了循環(huán)變量i的值在正常范圍內(nèi),從而避免了對指針pDBFat進行內(nèi)存越界的操作。從上面的測試過程中,我們可以看到:如此嚴重的問題,僅僅是一個簡單的錯誤引起的。實際上,系統(tǒng)的不穩(wěn)定往往是由這些看似很簡單的小錯誤導致的。這個問題給我們教訓的是:在直接對內(nèi)存地址進行操作時,一定要保證其值的合法性,否則容易引起內(nèi)存操作越界,給系統(tǒng)的穩(wěn)定性帶來潛在的威脅。【案例】近日在CDB并行測試中發(fā)現(xiàn)一個問題:我們需要的小區(qū)負荷話統(tǒng)結(jié)果總是為零,開始還以為小區(qū)負荷太小,于是加大短消息下發(fā)數(shù)量,但還為零,于是在程序中加入測試代碼,把收到的數(shù)據(jù)在BAM上打印出來,結(jié)果打印出來的數(shù)據(jù)正常,不可能為零,仔細查看相關(guān)代碼,問題只可能在指針移位上有問題,果然在函數(shù)中發(fā)現(xiàn)一處比較隱蔽的錯誤。/*功能:一個BM模塊內(nèi)所有小區(qū)CDB側(cè)廣播消息忙閑情況*//*************************************************************/voidCell_CBCH_Load_Static(structMsgCBFAR*pMsg){。。。 memcpy((_UC*)&tmp_msg,pMsg,sizeof(tmp_msg)); pMsg=pMsg+sizeof(tmp_msg);//sizeof(tmp_msg)=10;本意是想移動10個字節(jié),可是實際上指針移動了10*sizeof(structMsgCB)個字節(jié); CellNum=tmp_msg.usCellNum;。。。}1所以結(jié)構(gòu)指針傳入函數(shù)后,如要進行指針移動操作,最好先將其轉(zhuǎn)化為_UC型再說??傊羔槻僮饕⌒臑樯?。3、避免指針的非法引用【案例】【正文】在一次測試中,并沒有記得做了什么操作,發(fā)現(xiàn)HONET系統(tǒng)的主機復位了,之后,系統(tǒng)又工作正常了。由于沒有打開后臺的跟蹤窗口,當時查了半天沒有眉目。過了半天,現(xiàn)象又出現(xiàn)了,而且這次是主機在反復復位,系統(tǒng)根本無法正常工作了。我憑記憶,判斷應(yīng)該是與當時正在測試的DSL板的端口配置有關(guān)。于是將板上所有端口配置為普通2B+D端口,重新加載在主機數(shù)據(jù),現(xiàn)象消失。于是初步定位為主機在DSL端口處理過程中有重大錯誤。我在新的數(shù)據(jù)上努力恢復原出問題的現(xiàn)象,卻一直沒有重現(xiàn),于是恢復原數(shù)據(jù),加載后立即重現(xiàn)。并注意到,當DSL端口激活時,主機復位。仔細比較兩種數(shù)據(jù)的差別,發(fā)現(xiàn)出現(xiàn)主機復位問題的數(shù)據(jù)中DSL板配置了MNT/MLT端口,但是沒有做DSL端口之間的半永久數(shù)據(jù)。于是在程序中不斷加打印語句,通過后臺的DBWIN調(diào)試程序跟蹤,最后終于定位為:每當執(zhí)行到portdsl.c的DeviceDslMsgProc()函數(shù)中處理U口透傳的if(SPC_STATE_OK==pSpcCB->bySpcState)語句時,主機復位。但是該語句似乎并無不妥。再分析整個函數(shù),pSpcCB在函數(shù)前部分已經(jīng)被賦值,pSpcCB=SpcCB+(PortTable+index)->spcNo;但由于得到index后,沒有任何判斷,導致若MNT/MLT端口沒有做半永久,端口激活后,執(zhí)行此部分函數(shù),(PortTable+index)->spcNo有可能為NULL_WORD,于是,運算后,pSpcCB可能為非法值。此時主機在取進行判斷,就不知會導致什么后果了。其實,改起來很簡單,只要在這兩句前增加一個判斷就行了。于是,修改代碼為:if((PortTable+index)->spcNo!=NULL_WORD){ pSpcCB=SpcCB+(PortTable+index)->spcNo; if(SPC_STATE_OK==pSpcCB->bySpcState) {。。。}}修改后,問題不再重現(xiàn)。經(jīng)過分析可以發(fā)現(xiàn),編譯環(huán)境是有很大的容許空間的,若主機沒有做充分的保護,很可能會有極嚴重的隨即故障出現(xiàn)。所以編程時一定要考慮各種可能情況;而測試中遇到此類死機問題,則要耐心的定位到具體是執(zhí)行哪句代碼時出現(xiàn)的,再進行分析。因為問題很隱蔽,直接分析海一樣的代碼是很難發(fā)現(xiàn)的。4、變量類型定義錯誤【案例】【正文】在FRI板上建幾條FRPVC,其DLCI類型分別為:10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。相應(yīng)的DLCI值為:16、234、991、126975、1234567,然后保存,重起MUX,觀察PVC的恢復情況,結(jié)果DLCI值為16、234和991的PVC正確恢復,而DLCI=126975的PVC恢復的數(shù)據(jù)錯誤為61439,而DLCI=1234567的PVC完全沒有恢復。對于17/4類型,DLCI=126975的PVC在恢復時變成61439,根據(jù)這條線索,查找原因,發(fā)現(xiàn)126975-61439=65535,轉(zhuǎn)化二進制就是,也就是說在數(shù)據(jù)恢復或保存時把原數(shù)據(jù)的第一個1給忽略了。此時第一個想法是:在程序處理中,把無符號長整型變量當作短整型變量處理了,為了證實這個判斷,針對17bit/4bytes類型又重新設(shè)計測試用例:(1)先建PVC,DLCI=65535,然后保存,重起MUX,觀察PVC的恢復情況,發(fā)現(xiàn)PVC能夠正確恢復;(2)再建PVC,DLCI=65536,然后保存,重起MUX,觀察PVC的恢復情況,此時PVC不能正確恢復。至此基本可以斷定原因就是出在這里。帶著這個目的查看原代碼,發(fā)現(xiàn)在以下代碼中有問題:int _GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID){DWORDtempDlci; char szArg[80];1 char szLine[80]; ID LowPVCEP;DWORD dwDlciVal[5][2]= {{16,1007},{16,1007},{1024,64511}, {2048,129023},{131072,4194303}};...}typedefstructtagFrPppIntIWF{... 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;...}DWORDRestoreFrNetExtIWFData(WORDwSlotID,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;...}DWORDRestoreFrHdlcIntIWFData(WORDwSlotID,BYTE*pReadPoint){ WORD wCount,wTotalHdlcIWF; DWORD dwCCID,dwPeerCCID,dwAtmPort,dwPeerAtmPort; DWORD dwResult; BYTE bSlotID,bPeerSlotID; WORD wHdlcPort,wOldAtmPort,wCIR; WORD wPeerHdlcPort,wPeerOldAtmPort;...}其中涉及DLCI值的變量都為WORD(即無符號短整型)類型,在程序的處理時,出現(xiàn)WORD和DWORD(無符號長整型)類型在一句中同時存在的情況,至此可以判斷問題出在這里。由于DLCI值在不同類型時的取值范圍不同,前三種類型的取值范圍為16~991,第四種取值范圍為2048~126975,第五種取值范圍為131072~4194303,所以當采用前三種DLCI類型時,采用WORD類型最大值為65535,已經(jīng)完全夠用了;而對于第四種類型時,其取值在超過65535時,獲取DLCI值的函數(shù)_GetFrDlci()采用DWORD類型,而負責保存和恢復的兩個函數(shù)SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值當作WORD類型進行處理,因此導致DLCI取值越界,于是程序把原本為長整型的DLCI強制轉(zhuǎn)換成整型,從而導致DLCI值在恢復時,比原數(shù)據(jù)小65536。而在程序運行過程中,這些數(shù)據(jù)保存在DRAM中,程序運行直接從DRAM中獲取數(shù)據(jù),程序不會出錯;當FRI板復位或插拔后,需要從FLASH中讀取數(shù)據(jù),此時恢復函數(shù)的錯誤就表現(xiàn)出來。另一個問題是為什么23/4類型的DLCI數(shù)據(jù)不能恢復?這是由于對于23/4類型的PVC,其DLCI的取值范圍為:131072~4194303,而程序強制轉(zhuǎn)換并恢復的數(shù)據(jù)最大只能是65535,所以這條PVC不能恢復。至此,DLCI數(shù)據(jù)恢復出錯的原因完全找到,解決的方法是將DLCI的類型改為DWORD類型。從這個案例可以看出,在程序開發(fā)中一個很低級的錯誤,將在實際工作中造成很嚴重的后果?!景咐俊菊摹吭贔RI板上建幾條FRPVC,其DLCI類型分別為:10Bit/2bytes、10bit/3bytes、16bit/3bytes、17bit/4bytes、23bit/4bytes。相應(yīng)的DLCI值為:16、234、991、126975、1234567,然后保存,重起MUX,觀察PVC的恢復情況,結(jié)果DLCI值為16、234和991的PVC正確恢復,而DLCI=126975的PVC恢復的數(shù)據(jù)錯誤為61439,而DLCI=1234567的PVC完全沒有恢復。對于17/4類型,DLCI=126975的PVC在恢復時變成61439,根據(jù)這條線索,查找原因,發(fā)現(xiàn)126975-61439=65535,轉(zhuǎn)化二進制就是,也就是說在數(shù)據(jù)恢復或保存時把原數(shù)據(jù)的第一個1給忽略了。此時第一個想法是:在程序處理中,把無符號長整型變量當作短整型變量處理了,為了證實這個判斷,針對17bit/4bytes類型又重新設(shè)計測試用例:(1)先建PVC,DLCI=65535,然后保存,重起MUX,觀察PVC的恢復情況,發(fā)現(xiàn)PVC能夠正確恢復;(2)再建PVC,DLCI=65536,然后保存,重起MUX,觀察PVC的恢復情況,此時PVC不能正確恢復。至此基本可以斷定原因就是出在這里。帶著這個目的查看原代碼,發(fā)現(xiàn)在以下代碼中有問題:int _GetFrDlci(DWORD*dwDlci,char*str,DWORDdwDlciType,DWORDdwPortType,DWORDdwSlotID,DWORDdwPortID){DWORDtempDlci; char szArg[80]; char szLine[80]; ID LowPVCEP;DWORD dwDlciVal[5][2]= {{16,1007},{16,1007},{1024,64511}, {2048,129023},{131072,4194303}};...}typedefstructtagFrPppIntIWF{... 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;...}DWORDRestoreFrNetExtIWFData(WORDwSlotID,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;...}DWORDRestoreFrHdlcIntIWFData(WORDwSlotID,BYTE*pReadPoint){ WORD wCount,wTotalHdlcIWF; DWORD dwCCID,dwPeerCCID,dwAtmPort,dwPeerAtmPort; DWORD dwResult; BYTE bSlotID,bPeerSlotID; WORD wHdlcPort,wOldAtmPort,wCIR; WORD wPeerHdlcPort,wPeerOldAtmPort;...}其中涉及DLCI值的變量都為WORD(即無符號短整型)類型,在程序的處理時,出現(xiàn)WORD和DWORD(無符號長整型)類型在一句中同時存在的情況,至此可以判斷問題出在這里。由于DLCI值在不同類型時的取值范圍不同,前三種類型的取值范圍為16~991,第四種取值范圍為2048~126975,第五種取值范圍為131072~4194303,所以當采用前三種DLCI類型時,采用WORD類型最大值為65535,已經(jīng)完全夠用了;而對于第四種類型時,其取值在超過65535時,獲取DLCI值的函數(shù)_GetFrDlci()采用DWORD類型,而負責保存和恢復的兩個函數(shù)SaveFrNetExtIWFData()和RestoreFrNetExtIWFData(),都把DLCI的值當作WORD類型進行處理,因此導致DLCI取值越界,于是程序把原本為長整型的DLCI強制轉(zhuǎn)換成整型,從而導致DLCI值在恢復時,比原數(shù)據(jù)小65536。而在程序運行過程中,這些數(shù)據(jù)保存在DRAM中,程序運行直接從DRAM中獲取數(shù)據(jù),程序不會出錯;當FRI板復位或插拔后,需要從FLASH中讀取數(shù)據(jù),此時恢復函數(shù)的錯誤就表現(xiàn)出來。另一個問題是為什么23/4類型的DLCI數(shù)據(jù)不能恢復?這是由于對于23/4類型的PVC,其DLCI的取值范圍為:131072~4194303,而程序強制轉(zhuǎn)換并恢復的數(shù)據(jù)最大只能是65535,所以這條PVC不能恢復。至此,DLCI數(shù)據(jù)恢復出錯的原因完全找到,解決的方法是將DLCI的類型改為DWORD類型。從這個案例可以看出,在程序開發(fā)中一個很低級的錯誤,將在實際工作中造成很嚴重的后果。5、正確使用邏輯與&&、屏蔽&操作符【案例】【案例描述】:由于C語言中位與比求模效率高,因而系統(tǒng)設(shè)計時,對于模128的地方都改為與127,系統(tǒng)定義的宏為#defineMOD128127和#defineW_MOD127(定義的宏的名字易引起誤解),但實際程序中還是采取求模,從而引起發(fā)送窗口欲重發(fā)的和實際重發(fā)的不一致,最終導致鏈路復位此類嚴重問題,曾在定位此問題時花了不少時間?!咎幚磉^程】:處理過程如下:#defineMOD128127//隊列長128,當隊頭到128時,上其返回。#defineW_MOD127//發(fā)送窗口隊列,意義同上。在函數(shù)L2_TO_L1()中,有如下語句:linkstate_ptr->SendWin.head=(head+1)%W_MOD;這里當head=126時,SendWin.head=0,這將造成發(fā)送窗口指針和隊列窗口指針錯位,造成鏈路復位;另外,在重發(fā)函數(shù)voidINVOKE_RETRANSMISSION(_USlogic_link,_USn_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;第一個語句求欲重發(fā)的消息包個數(shù),第二個語句求重發(fā)的起始位置,當Vs小于n_r時,將造成實際重發(fā)數(shù)小于欲重發(fā)數(shù),同時造成實際起始重發(fā)位置和欲重發(fā)起始位置錯開,從而引起鏈路復位。上面三個語句應(yīng)該做如下改動: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;【結(jié)論】:由于鏈路通信對系統(tǒng)效率要求很高,算法采用效率最高的,但位與(&)和求模(%)這小小的區(qū)別,造成的竟是鏈路復位這種嚴重的錯誤。【思考與啟示】:對這類問題,大家在閱讀代碼或代碼審查時一定要注意,仔細一點往往能發(fā)現(xiàn)問題,但在測試中來定位這種問題,花費的時間往往更長。6、注意數(shù)據(jù)類型的匹配【案例】【案例描述】下面通過測試中的一個例子來說明這個問題:命令DSPN7C是用來顯示NO7電路狀態(tài)的,其參數(shù)設(shè)備類型DID支持TUP和ISUP,參數(shù)信道號BSN支持多值輸入(最多支持32路查詢),正常情況下該命令沒有問題。但試了非正常情況下,問題就出來了。 1、首先試BSN參數(shù)越界情況,即參數(shù)BSN超過32路查詢,選了幾個數(shù)據(jù)段,問題就出來了。對于0&&300和0&&256,該命令返回結(jié)果不一致,對前者認為參數(shù)越界,對后者返回執(zhí)行成功。 2、對于參數(shù)DID,選定一種設(shè)備類型(TUP或ISUP),讓參數(shù)BSN所包含的32路電路跨越TUP和ISUP,兩次結(jié)果是不一致的?!咎幚磉^程】反饋到開發(fā)人員那里,第一個問題是BAM的問題,第二個問題是SM的問題?!窘Y(jié)論】 1、為數(shù)據(jù)超出范圍溢出造成,int值賦值給BYTE,造成數(shù)據(jù)丟失。 2、問題的產(chǎn)生是因為查詢的第一個信道是TUP電路,但是卻按ISUP電路查詢。ISUP的維護處理函數(shù)判斷第一個信道不是ISUP信道,認為整個的PCM不是ISUP類型的PCM,返回全部的電路狀態(tài)為未安裝。消息處理不合理。TUP也會產(chǎn)生如此錯誤?!舅伎寂c啟示】我們的MML命令并不是無懈可擊的,許多表面上的小問題,往往隱藏著代碼的缺陷和錯誤?!景咐俊菊摹慨斘覀兪褂肞C-LINT檢查代碼時,會發(fā)現(xiàn)大量的數(shù)據(jù)類型不匹配的告警,大部分情況下,這種代碼上存在的問題并不會引起程序功能實現(xiàn)上的錯誤,但有些情況下,也許會產(chǎn)生嚴重的問題:一、不同數(shù)據(jù)類型變量之間賦值引起的問題,實際上,該類問題也可以分為幾種情況:1、直接賦值,比如,把一個WORD型變量賦給一個INT型變量,如果WORD型變量大于32767,INT型變量得到的就是一個負值了?!纠弧恳淮螠y試過程中發(fā)現(xiàn),SDH送的告警在BAM調(diào)試窗口打印出紅色提示:File(XXX),Line(XXX):Invalidalarmid,from:7,AlarmId:65463經(jīng)過檢查數(shù)據(jù)發(fā)現(xiàn),并沒有ID為65463的告警,分析上報的數(shù)據(jù)幀,發(fā)現(xiàn)上報的告警ID為B7,原來代碼中有一處強制類型轉(zhuǎn)換:sdhAlmStru.AlarmId=(WORD)RecvBuffer[iTmpLen+5];char型強制轉(zhuǎn)換成WORD型。B7就變成了FFB7,十進制就是65463。由于char是有符號型,B7的第8位為1,所以轉(zhuǎn)換后為FFB7,而不是代碼作者希望的00B7,如果第8位是0,或該變量是BYTE型,轉(zhuǎn)換就不會有問題了。2、函數(shù)形參和實參不一致,實際上和第一種情況本質(zhì)上是一樣的,只是表現(xiàn)的形式不太一樣,這種情況也是代碼中經(jīng)常出現(xiàn)的問題,下面例子是測試中曾經(jīng)發(fā)現(xiàn)的一個小問題:【例二】在file01中的INTDebugMsgProc(charbyMsg0,charbyMsg1)函數(shù),兩個形參都是char型,而實際傳入的參數(shù)都是BYTE型,結(jié)果函數(shù)中的如下語句:PrintfE(PID_RED,"%dtickstimeout!",byMsg1);在byMsg1大于127時,輸出錯誤的結(jié)果。二、不同數(shù)據(jù)類型之間的比較操作在循環(huán)終止條件的判斷中,不同類型變量的比較操作是容易造成死循環(huán)錯誤的地方,同時也是開發(fā)人員容易忽視的地方,值得測試人員多加留意。下面兩個例子是該類錯誤的兩種典型情況:【例三】file02文件中某函數(shù)中如下代碼,可能造成死循環(huán):......inti; WORD*pCheck=(WORD*)p; WORDwCheckSum=*pCheck; pCheck++; for(i=1;i<dwLen/2;i++) { wCheckSum^=(*pCheck); pCheck++; } //binlenhadalreadywordalignment return(wCheckSum); ......該段代碼是在DOS環(huán)境下用BC編譯的,由于循環(huán)變量i是int型(2個字節(jié)),而dwLen是DWORD型(4個字節(jié)),如果dwLen大于65536,那么該函數(shù)就是死循環(huán)了。上面的例子是不同類型變量之間直接比較操作,還有一種情況是函數(shù)的返回值與另一不同類型的變量比較,見下面例子:【例四】file03.c文件中某函數(shù)中如下代碼, while(ftell(fp)<Part[3]) {.....} ftell返回long型,而Part是DWORD型,有符號變量和無符號變量的比較,可能造成死循環(huán)。類似的例子還有很多,類型不匹配的問題還有許多種情況,都是代碼中的隱患,有時會造成嚴重的后果,需要引起足夠的重視。對于該類問題,我們可以利用PC-LINT工具對代碼進行細致的檢查。7、用于控制條件轉(zhuǎn)移的表達式及取值范圍是否書寫正確【案例】【案例描述】:在測試主機MPU板倒換功能時,如果MPU備份充分,倒換前后對處于激活狀態(tài)的電路應(yīng)無影響,即不影響通話。但近期測試發(fā)現(xiàn),如果兩局通過DT板進行一號對接,MPU備份倒換卻發(fā)生斷話。具體現(xiàn)象為:如果DT板的第1個PCM系統(tǒng)電路為故障,則MPU倒換時復位該DT板,如果DT板的第2個PCM系統(tǒng)電路為故障,則MPU倒換時復位下一塊DT。【處理過程】:據(jù)查,MPU倒換時會自動復位處于“故障”態(tài)的電路,但由于計算錯誤(多加了32),錯復位了下一個PCM系統(tǒng)32路電路?!窘Y(jié)論】:如此嚴重問題為什么到今天才發(fā)現(xiàn)?因為我們在實驗室中一般采用同一單板的2個PCM系統(tǒng)自環(huán)進行測試,則不會在某單板上有故障和空閑電路共存,自環(huán)屏蔽了錯誤?!舅伎寂c啟示】:自環(huán)是在測試環(huán)境下常用的一種提高效率的手段,但一旦條件允許,我們的測試工作應(yīng)盡量模擬網(wǎng)上的實際環(huán)境進行?!景咐科綍r對計費功能進行測試的時候,瀏覽詳細話單都是比較注意話單本身的正確性,并沒有注意該命令對系統(tǒng)的影響。所以當瀏覽少量話單的時候,并沒有發(fā)現(xiàn)該命令的異常。但是當時間的跨度較大時,詳細話單數(shù)量較多,問題就出現(xiàn)了。執(zhí)行如下命令: LSTAMA:TP=NRM,當瀏覽了大約10萬張詳細話單后,終端與BAM的連接關(guān)閉。重建連接后,發(fā)現(xiàn)話單臺的命令不能執(zhí)行。觀察BAM的性能,發(fā)現(xiàn)話單臺仍占有CPU50%以上的利用率,說明原來的任務(wù)仍在執(zhí)行。需要關(guān)一下話單臺才能恢復正常。重復上述步驟,當終端與BAM的連接尚未關(guān)閉時主動斷開此次連接,結(jié)果同上。反饋到開發(fā)人員那里,發(fā)現(xiàn)該現(xiàn)象與設(shè)計的初衷是相違背的。本來話單臺控制最多輸出200張話單,這是為了防止過多話單的輸出顯示會增加BAM的開銷,從而降低BAM的性能。查看一下源代碼,問題就發(fā)現(xiàn)了。話單臺控制最多輸出200張話單程序如下while(timeCur<=timeEnd){ timeCur+=tsOneDay;//加一天 while(fileBill.Read(&rpt,sizeof(CBillReport))== sizeof(CBillReport)) {..................... //只輸出滿足條件的前200張話單 if(++wBillCount==200) { break; } }//一個文件查詢結(jié)束}//所有文件查詢結(jié)束在話單輸出200張之后,程序只退出一層循環(huán),仍然會從下一天話單繼續(xù)輸出,導致向MML發(fā)幀過多,造成MML和話單臺都被堵死。修改ProcessQueryBill()函數(shù)//只輸出滿足條件的前200張話單if(++wBillCount==200){ timeCur=timeEnd+tsOneDay;//退出第二層循環(huán), while(timeCur<=timeEnd) break;}作上述修改后問題就不再出現(xiàn)了。一些MML命令從完成的功能來講可能是沒什么問題的,但其執(zhí)行對系統(tǒng)性能的影響我們在測試時時往往給忽視了。在我們目前的BAM方案中,存在著多個終端協(xié)同工作,如果某個終端發(fā)出的命令在BAM中長時間獨占著大部分系統(tǒng)資源,造成的后果是嚴重的。這是在設(shè)計時要避免的,在測試中要注意的問題?!景咐俊菊摹吭谂袛嗄M用戶端口是否反極性時有這樣一段程序: if((bsn>=g_wASL32StartPSN)&&(((bsn-g_wASL32StartPSN)%32)==15||((bsn-g_wASL32StartPSN)%32==16))) returnTRUE; if((bsn%16)==7||(bsn%16)==8) returnTRUE; returnFALSE;作者的本意是如果是32路用戶板(藍色字體判斷),就看端口號是否是第15和16路,如果是,就是反極性端口,返回TRUE,否則就不是,應(yīng)該返回FALSE。但代碼表達的意思是:如果是32路用戶板并且端口號是15或16就返回真值,否則還要執(zhí)行下邊語句。當端口在32路用戶板上,但端口號不是15或16時,不同的32路端口的起始地址g_wASL32StartPSN,會導致不同的非15、16端口被誤認為是反極性端口。舉個例子,當g_wASL32StartPSN的值為3000時,端口號為3000(第一塊板上的第0個端口)就被認為是反極性端口,這與作者的意圖完全相悖??梢詫⒋a修改如下: if((bsn>=g_wASL32StartPSN) { if(((bsn-g_wASL32StartPSN)%32)==15||((bsn-g_wASL32StartPSN)%32==16))) returnTRUE; } else if((bsn%16)==7||(bsn%16)==8) returnTRUE; returnFALSE;通過這個例子,我覺得在代碼審查時應(yīng)該留意在判斷條件較多的情況下,每個輸入是否都能正確輸出,在單元測試、集成測試、系統(tǒng)測試時要針對邊界值設(shè)計相應(yīng)的測試用例。判斷條件較多時開發(fā)人員也應(yīng)該適當分開寫,既使代碼更易讀,又不容易出錯。8、條件分支處理是否有遺漏【案例】【現(xiàn)象】在接入網(wǎng)主機程序的代碼審查中,發(fā)現(xiàn)dbquery.c的DBQ_Init_ANType函數(shù)中如下代碼段缺少應(yīng)有的條件分支,在數(shù)據(jù)異常的情況下,會產(chǎn)生較嚴重的問題?!咎幚磉^程】該錯誤比較隱蔽,現(xiàn)在說明如下:Max2B1QStatTime最大統(tǒng)計時間Max2B1QStatPortNum最大統(tǒng)計端口數(shù)MAX_2B1Q_STAT_PSN最大統(tǒng)計內(nèi)存分配數(shù)量其中:Max2B1QStatTime(最大統(tǒng)計時間)和Max2B1QStatPortNum(最大統(tǒng)計端口數(shù))的乘積不能大于MAX_2B1Q_STAT_PSN程序如下://查詢數(shù)據(jù)庫,獲得Max2B1QStatTime的值 directQueryCond.tupleNo=10; error_code=DB_Query(RID_OTHERS_PARA_INFO,1, (LPDBCondition)&directQueryCond, (BYTEFAR*)&tempstruct0); //查詢數(shù)據(jù)庫成功 if(error_code==DB_SUCCESS) { //tempstruct0.data是數(shù)據(jù)庫中為Max2B1QStatTime配置的值 if(tempstruct0.data>MAX_2B1Q_STAT_PSN) Max2B1QStatTime=MAX_2B1Q_STAT_PSN; elseif(tempstruct0.data!=0) Max2B1QStatTime=tempstruct0.data; }//查詢數(shù)據(jù)庫,獲得Max2B1QStatPortNum的值 directQueryCond.tupleNo=11; error_code=DB_Query(RID_OTHERS_PARA_INFO,1, (LPDBCondition)&directQueryCond, (BYTEFAR*)&tempstruct0); //查詢數(shù)據(jù)庫成功 if(error_code==DB_SUCCESS) { //tempstruct0.data為數(shù)據(jù)庫中為Max2B1QStatPortNum配置的值,如果其缺省值和Max2B1QStatTime乘積值大于MAX_2B1Q_STAT_PSN的話: if((tempstruct0.data*Max2B1QStatTime)>MAX_2B1Q_STAT_PSN) Max2B1QStatPortNum=MAX_2B1Q_STAT_PSN/Max2B1QStatTime;//如果在合理范圍內(nèi)且不為0的話:elseif(tempstruct0.data!=0) Max2B1QStatPortNum=tempstruct0.data; }此處if-elseif分支沒有判斷值為0的情況,即數(shù)據(jù)庫為Max2B1QStatPortNum配置的值為0:tempstruct0.data==0,則Max2B1QStatPortNum就為缺省值32?!窘Y(jié)論】由于內(nèi)存限制,Max2B1QStatTime(最大統(tǒng)計時間)和Max2B1QStatPortNum(最大統(tǒng)計端口數(shù))的乘積不能大于MAX_2B1Q_STAT_PSN,如果從數(shù)據(jù)庫中得到Max2B1QStatTime為MAX_2B1Q_STAT_PSN,而數(shù)據(jù)庫中最大統(tǒng)計端口數(shù)恰好為0,由于上述代碼沒有對tempstruct0.data==0的情況進行判斷,Max2B1QStatPortNum為缺省值32,這樣Max2B1QStatTime和Max2B1QStatPortNum乘積已經(jīng)是32倍MAX_2B1Q_STAT_PSN了,遠遠超過了設(shè)計內(nèi)存的限制。造成這種錯誤的原因是判斷語句對條件判斷不完整。【思考與啟示】在代碼審查時,應(yīng)該十分注意條件判斷的的完備性。好多問題就是因為條件判斷不完全造成的。9、引用已釋放的資源【案例】【正文】在計費測試的過程中,用呼叫器進行大話務(wù)量呼叫測試。30路話路通過TUP自環(huán)呼叫另外30路話路,計費數(shù)據(jù)的設(shè)定是這樣的:通過計費情況索引對主叫計費,得到詳細話單。首先保證計費數(shù)據(jù)設(shè)定的正確性,打了幾次自環(huán)后,查看話單正常,則開始呼叫。呼叫幾萬次后停止呼叫,取話單進行觀察。發(fā)現(xiàn)這30路每次呼叫總會出現(xiàn)一張告警話單,其余話單正常,該告警話單相對于話路來說是隨機出現(xiàn)的。通知開發(fā)人員后,首先我們再次對計費數(shù)據(jù)進行了確認。某個用戶在某次呼叫產(chǎn)生了告警話單,其上一次和下一次呼叫的計費情況都正常,兩次呼叫之間的時間間隔只有幾秒鐘,排除了人為修改數(shù)據(jù)的可能。開發(fā)人員認為是CCB的問題,后來一查果然如此。當中繼選線發(fā)生了同搶需要重新選線時,CCB的reset_CCB_for_reseatch_called_location()就會把有關(guān)的呼叫信息清掉,造成計費情況分析失敗,產(chǎn)生計費費用為0的告警話單。更正reset_CCB_for_reseatch_called_location()中清除被叫信息的代碼,重選中繼時不清除被叫用戶這部分屬性。思考與啟示: 1、在計費測試過程中,對話單的觀察很重要,不應(yīng)該放過任何一個細小的疑點; 2、計費測試僅僅打幾次往往達不到效果,越接近用戶實際使用的情況越可能發(fā)現(xiàn)問題。【案例】【案例描述】在進行128模塊V5用戶CENTREX新業(yè)務(wù)測試時,偶然遇到一個怪現(xiàn)象:對群內(nèi)一個V5ST用戶只開放MCT權(quán)限,在進行惡意呼叫追查時,有一次報惡意呼叫追查成功音只報了一半,當正要報出惡意呼叫的號碼時,業(yè)務(wù)中斷重新回到通話態(tài),隨即重新追查一次,報“已申請其它新業(yè)務(wù),本次申請不成功”。惡意呼叫追查與任何新業(yè)務(wù)都不會沖突,而且此用戶也只有惡意呼叫追查有權(quán),可以肯定此時程序出問題了。為了重現(xiàn),再次掛機,重新呼叫,應(yīng)用此新業(yè)務(wù),但這個現(xiàn)象一直沒有出現(xiàn)。大約反復操作20遍,又出現(xiàn)了一次這樣的情況,顯然程序中可能存在某種問題?!咎幚磉^程】出現(xiàn)這個問題后,及時與開發(fā)人員A取得了聯(lián)系,并一起試圖重現(xiàn)這個問題,通過許多次的反復操作,又出現(xiàn)了一次這種情況。確認問題后,A表現(xiàn)出高度的責任心,馬上駕調(diào)試環(huán)境,反復調(diào)測,終于在當天就逮住了狐貍尾巴:1、當用戶接聽惡意呼叫者的,并啟動惡意呼叫追查業(yè)務(wù)后,在V5_CR_VOICETONE狀態(tài)下,只要聽MCT音的用戶用脈沖方式撥任意一個數(shù)字,則立即停止送MCT音,而將用戶切換回與惡意呼叫者的通話.但是程序中沒有對撥號類型作判斷,導致用戶若用音頻撥號也會作同樣的處理。2、除了取消此次MCT服務(wù),將用戶切換回與惡意呼叫者的通話外,如果不釋放MCT_HANDLE,由于每個模塊只有一個這樣的資源,則下一次使用MCT業(yè)務(wù)的用戶不能成功,因為會在申請MCT_HANDLE時失敗,V5模塊和ST模塊在這個地方處理都有問題,沒有將MCT_HANDLE釋放掉,對于V5用戶會聽新業(yè)務(wù)失敗音,對于ST用戶會聽音樂。當不停的撥測V5用戶的MCT業(yè)務(wù)時,有時在聽音時,可能由于網(wǎng)板有雜音等原因(或用戶碰了話機的按鍵),導致DTR收到一位號,則會立即停止此次MCT服務(wù),用戶會聽到MCT送音突然中斷,然后恢復了與惡意呼叫者的通話.而下次再用MCT時,由于上面所述的原因,會聽到新業(yè)務(wù)失敗音,此次失敗后,無論MCT_HANDLE分配成功與否,該用戶的MCT標志都被置為1,所以在用戶掛機時,會將該模塊唯一的MCT_HANDLE資源釋放掉.則以后該功能又可以正常實現(xiàn)。在追查這個問題時,開發(fā)人員A又發(fā)現(xiàn)了一個可能導致死機的嚴重問題:在用戶啟動MCT服務(wù),正在聽報追查號碼的MCT音時,若惡意用戶此時掛機,CCB的處理中,只針對ST用戶送DISCONNECT,而對V5ST用戶送的是RELEASE消息,這導致V5X收到此消息后,將該V5ST用戶的cr2清除掉,V5_USER_TALBE[].cr2變?yōu)?xFFFF,這樣在V5_CR_VOICETONE超時后,程序中會檢查cr2的狀態(tài)是否為HOLD,當取cr2的內(nèi)容時,由于cr2已被清除,會發(fā)生指針越界的GP錯誤?!窘Y(jié)論】通過調(diào)測發(fā)現(xiàn)、定位并解決問題。【思考與啟示】我們平常一些熟視無睹的業(yè)務(wù)或按正常流程操作沒有問題的業(yè)務(wù),不能保證它就一定沒有問題,要善于抓住一絲一毫的異常現(xiàn)象。對于很難重現(xiàn)的問題千萬不要輕易放過,我們網(wǎng)上設(shè)備所出的問題很多都是一些在實驗室難以出現(xiàn)或很難重現(xiàn)的一些問題,一些顯而易見的問題一般都可消滅在實驗室,難就難在消滅一些隱藏很深的問題。說老實話,我們的產(chǎn)品還有許多問題,需要我們扎扎實實鍥而不舍的工作。10、分配資源是否已正確釋放【案例】【正文】在對接入網(wǎng)A產(chǎn)品的網(wǎng)管軟件測試中,發(fā)現(xiàn)了一個WINDSOWS資源損耗的的問題:當網(wǎng)管軟件運行幾天后,WINDOWS總會出現(xiàn)“資源不夠”的告警提示。如果網(wǎng)管軟件不關(guān)掉再重新啟動的話,就會出現(xiàn)WINDOWS資源完全耗盡的現(xiàn)象,最終網(wǎng)管系統(tǒng)反應(yīng)很慢,無法正常工作。從現(xiàn)象上可以判斷出,網(wǎng)管軟件存在隱蔽的內(nèi)存泄露或資源不釋放的問題,并且這種資源耗盡是一個緩慢的過程。如何定位這個問題呢?定位這種問題可以利用WINDOWS中的一個系統(tǒng)資源監(jiān)視工具。打開Windows的“附件/系統(tǒng)工具/資源狀況”,這是一個系統(tǒng)資源、用戶資源、和GDI資源的實時監(jiān)視工具。工具有了,那么如何發(fā)現(xiàn)導致不斷消耗資源的特定操作呢?首先和開發(fā)人員共同探討,列出幾個最可能消耗資源的操作和一些操作組合,這樣就縮小了監(jiān)視范圍,避免沒有范圍的碰運氣,否則如大海撈針。監(jiān)視前,首先重新啟動WINDOWS,最好不運行其他的程序,打開“系統(tǒng)狀況”這個監(jiān)視工具,然后運行網(wǎng)管軟件,記下此時的資源狀況數(shù)據(jù)。然后針對一個可疑的操作,快速大量地重復進行。這種重復性的操作可以利用QArun測試工具執(zhí)行,QArun可以記錄操作者的一次操作步驟,然后按照設(shè)定的次數(shù)重復操作。操作后,觀察此時的資源狀況,并記下此時的數(shù)據(jù),與操作前的數(shù)據(jù)比較,如果操作前后的數(shù)據(jù)數(shù)據(jù)沒有變化或變化很小,可排除此項操作,否則就可斷定此項操作會引起資源耗盡。對其它可疑的操作和操作組合重復以上過程。通過以上的步驟,終于找出引起資源耗盡的罪魁禍首。分析相應(yīng)部分的代碼,發(fā)現(xiàn)引起資源耗盡原因有:內(nèi)存泄露,畫筆和畫刷資源用完后未釋放等?!景咐俊菊摹磕钞a(chǎn)品后臺軟件版本,是用C++寫的,程序員在寫代碼時,經(jīng)常在構(gòu)造函數(shù)中申請一塊內(nèi)存,而不釋放,在程序其他代碼中也經(jīng)常只管申請,不管釋放。例如:voidWarnSvr::SaveWarnData(){...... for(intm=0;m<RecordsInBuffer[EVENT_ALARM];m++) { HISTORY_FILTER_INDEX*item=

newHISTORY_FILTER_INDEX; item->Csn=Buffer[EVENT_ALARM][m].Csn;

item->Position=m

+(RecordsInHistoryFile-RecordsInBuffer[EVENT_ALARM]); //IfawarnwithacertainCsnisnotinEventFilterIndex //itisnotnecessarytobeaddedtoHistoryFilterIndex intitem_total=EventFilterIndex.GetItemsInContainer(); BOOLfind_flag=false; for(intk=0;k<item_total;k++) if(EventFilterIndex[k]->Csn==item->Csn) { find_flag=true; break; } if(find_flag) { HistoryFilterIndex.Add(item); if(HistoryFilterIndex.IsFull()) ClearIndexEntry(); }//建議在此處加上://else//deleteitem;}。有的程序員認為,后臺運行的環(huán)境有大量內(nèi)存,幾個字節(jié)的浪費不會造成死機等重大事故。然而,長時間累計起來,必然會造成資源緊張而出現(xiàn)故障。實際上,這種思想是造成我們產(chǎn)品不穩(wěn)定的原因之一。我們的主機在網(wǎng)上能運行幾個月幾年,大家對內(nèi)存的分配釋放較敏感,而我們的后臺產(chǎn)品往往只能正常運行幾天。這個地方不注意也是原因之一吧?!景咐俊菊摹吭谶M行代碼審查過程中,造成內(nèi)存泄漏的代碼比較多。下面舉幾種常見的內(nèi)存泄漏錯誤,供測試人員在代碼審查中參考: 1.函數(shù)有多個出口時,沒有在每個出口處對動態(tài)申請的內(nèi)存進行釋放。一般在異常處理時容易出現(xiàn)這種錯誤。下面的代碼段就是這樣的例子:.....pRecord=newchar[pTable->GetRecordLength()]; assert(pRecord!=NULL); if(pTable->GoTop(FALSE)!=DBIERR_NONE) return;//如果從這里返回,pRecord將得不到釋放.....pTable->Close(); delete[]pRecord;} 2.給指針賦值時,沒有檢查指針是否為空,如果指針不為空,那么指針原來指向的內(nèi)存將丟失。請看如下代碼段:.... structFileInfo*pdbffile=newstructFileInfo; pdbffile->pfileinfo=newstructffblk; pdbffile->srcname=srcRootPath; pdbffile->desname=desRootPath; pdbffile->prev=NULL; pfile=pdbffile;//賦值之前沒有檢查一下pfile是否為空,如果不為空,會造成pfile指向的內(nèi)存丟失。 dbf_start_needed=FALSE; dbf_Finish=FALSE; flag_begined=TRUE; if(FALSE==Copy(TRUE)) { dbf_start_needed=TRUE; WarnMsgOut("Erroroccurswhilecopyingfilesindirectory<dbf>,tryingagain."); }} 3.連續(xù)二次內(nèi)存動態(tài)分配,在第二次分配失敗時,忘記釋放第一次已經(jīng)申請到的內(nèi)存。 .... pMsgDB_DEV=(PDBDevMsg)GetBuff(sizeof(DBDevMsg),__LINE__); if(pMsgDB_DEV==NULL) return; pMsgDBApp_To_Logic=(LPDBSelfMsg)GetBuff(sizeof(DBSelfMsg),__LINE__); if(pMsgDBApp_To_Logic==NULL) return;//此處返回造成pMsgDB_DEV指向的內(nèi)存丟失 .... 4.代碼中缺少應(yīng)有的條件分支處理,導致程序未執(zhí)行任何操作而退出時,也可能沒有釋放應(yīng)釋放的內(nèi)存,這種情況一般是缺少應(yīng)有的else分支,或switch語句的default分支沒有應(yīng)有的處理。staticvoidOncePowerCmdHandle(structHT_Appmsg*msg){ ...... pPower_test_answer=(struct_oncepower_test_answer*)GetBuff(sizeof(struct_oncepower_test_answer),__LINE__); if(pPower_test_answer==NULL_PTR) return; ...... if(TSS_State[testpsn].state==TEST_DEV_BUSY|| TSS_State[testpsn].state==TEST_DEV_ERROR) {... } elseif(TSS_State[testpsn].state==TEST_DEV_IDLE) {... } //缺少else分支,可能造成pPower_test_answer得不到釋放}造成內(nèi)存泄漏的情況很多,以上是幾種典型的情況。雖然內(nèi)存泄露一般出現(xiàn)在異常情況下,畢竟給系統(tǒng)造成很大的隱患,使系統(tǒng)的健壯性降低。測試人員在作代碼審查時,對上述幾種情況要尤其注意?!景咐俊菊摹吭谶M行SAR的PDU包發(fā)收的測試過程中要同時考慮幾個邊界值,即發(fā)送包大小范圍[0-Nmax],SAR的PDU包接收的最大值Kmax,MBUF塊的大小M.在實測中,將SAR的PDU包接收的最大值設(shè)為2000(Kmax=2000B),MBUF的塊長設(shè)為512(M=512B),則發(fā)送包大小的正確分支的取值為下限0,上限Nmax=2000,然后在0與2000之間隨機取若干值,再考慮MBUF的塊長,還可增加M倍數(shù)的若干選值及其附近值.以上是測試的一般思路,但由于很偶然的機會選擇包長2000,及Kmax=2000B,才發(fā)現(xiàn)問題.原因如下:MBUF塊長512,但塊中實際存放數(shù)據(jù)的只有500(MBUF頭上有2個長字,尾部有1個長字共12B只用于塊控制),而發(fā)送的包長正好是500的整數(shù)倍4,由于是整數(shù)倍,所以SAR(BT8230)從FREE鏈上摘成5個MBUF(原因從略),而SAR驅(qū)動只知道有4個MBUF,這樣到上層用戶時,只釋放4個MBUF,從而漏掉1個MBUF,經(jīng)過很短一段時間后,內(nèi)存即被耗盡.(此問題非常嚴重,因為在實際運用中,是500的整數(shù)倍的PDU包的概率較小,但一旦出現(xiàn)就會發(fā)生一次內(nèi)存泄漏,這樣經(jīng)過若干天或若干月的運行后會使系統(tǒng)崩潰)以前未發(fā)現(xiàn)此問題的原因是因為原來使用的緩沖塊長為2048,減去12B的控制信息,實際存放數(shù)據(jù)的長度為2036.由于只考慮了2048這個值,忽略了2036,所以在選取上下限中的若干值時,選取包的長度是2036的倍數(shù)的概率就非常小,因而未發(fā)現(xiàn)該問題.由于測試中一般很難將取值范圍中的所有值覆蓋全,所以在選取上下限中的若干取值時要格外仔細,考慮的方面盡可能全,因為很有可能其中某些值就是測試邊界值.凡是涉及的數(shù)字盡量選取,象該例中正確分支的測試邊界為0,2000,512及其整數(shù)倍,500及其整數(shù)倍,12及其整數(shù)倍等值,它們是必測的邊界值,而非可測可不測的隨機選取的所謂若干選值.【案例】【正文】ABIS.CPP中的函數(shù)rel_ABIS_CCB_conn()中,在進行消息鏈表Msg_Queue[ces]的拆鏈操作時,對于相應(yīng)的CCB只進行了一次拆鏈操作,即只拆除了一個節(jié)點,如果出現(xiàn)該CCB對應(yīng)的消息節(jié)點不止一個的情況就會出現(xiàn)大量節(jié)點不能釋放的問題。if(Msg_Queue[ces].msghead!=NULL_PTR)//messagebuffernotempty{//getfirstmessagerecordpMsgRecord=Msg_Queue[ces].msghead;//releasebuffer-messagesconcerningwithccb_nofor(index=0;index<MSGBUFFERNUM;index++){//這里要對pMsgRecord的值進行判斷if((pMsgRecord!=NULL_PTR)&&pMsgRecord->CCB_no==ccb_no){//freethemessagebufferif(pMsgRecord==Msg_Queue[ces].msghead)//headMsg_Queue[ces].msghead=pMsgRecord->pnext;elseif(pMsgRecord==Msg_Queue[ces].msgtail)//tail{Msg_Queue[ces].msgtail=pPrevMsgRecord;Msg_Queue[ces].msgtail->pnext=NULL_PTR;}else//notheadandtail{pPrevMsgRecord->pnext=pMsgRecord->pnext;}//putbufferbacktobufferpoolif(Msg_Buffer.empty_num==0){Msg_Buffer.linkhead=Msg_Buffer.linktail=pMsgRecord;pMsgRecord->pnext=NULL_PTR;//這里將pMsgRecord->pnext置為空Msg_Buffer.empty_num++;}else

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論