查找嵌入式C語言程序/軟件中的缺陷的多種技術(shù)
void handleSensorValue(int value)
{
initialize();
int index = -1;
if (value = 10) {
index = VALUE_LOW;
} else {
index = VALUE_HIGH;
}
printMessage(index, value);
}
相同地,我們也對最后一個(gè)報(bào)告的錯(cuò)誤進(jìn)行相應(yīng)的處理。現(xiàn)在我們再次運(yùn)行數(shù)據(jù)流分析,將不會再有錯(cuò)誤被報(bào)告出來。
為了確保程序運(yùn)行一切正常,我們重新運(yùn)行整個(gè)分析過程。首先,我們開啟運(yùn)行時(shí)內(nèi)存監(jiān)測并運(yùn)行應(yīng)用程序,一切表現(xiàn)正常。然后我們開啟內(nèi)存監(jiān)測并運(yùn)行單元測試,一個(gè)任務(wù)被報(bào)告出來:
本文引用地址:http://www.butianyuan.cn/article/151316.htm
我們的單元測試檢測到reportSensorFailure()函數(shù)的行為已經(jīng)發(fā)生了改變。這是由于我們已經(jīng)對finalize()函數(shù)進(jìn)行了修改——為了糾正之前報(bào)告的一個(gè)問題所做的修改。此處報(bào)告的任務(wù)是為了讓我們注意此修改,并提示我們應(yīng)該對測試用例進(jìn)行相應(yīng)的審查,并且確定是否應(yīng)該對代碼或者測試用例進(jìn)行相應(yīng)的修改,以表示這種新的行為實(shí)際上是我們所預(yù)期的行為。在檢查完代碼之后,我們發(fā)現(xiàn)后者(修改)是正確的并且應(yīng)該更新斷言的正確條件。
/* CPPtest_TEST_CASE_BEGIN test_reportSensorFailure */
/* CPPTEST_TEST_CASE_CONTEXT void reportSensorFailure(void) */
void sensor_tests_test_reportSensorFailure()
{
/* Pre-condition initialization */
/* Initializing global variable messages */
{
messages = 0 ;
}
{
/* Tested function call */
reportSensorFailure();
/* Post-condition check */
CPPTEST_ASSERT(0 == ( messages ));
}
}
/* CPPTEST_TEST_CASE_END test_reportSensorFailure */
作為最終的確認(rèn),我們需要獨(dú)立地運(yùn)行整個(gè)程序——在IDE中關(guān)閉掉運(yùn)行時(shí)內(nèi)存監(jiān)測來對程序進(jìn)行構(gòu)建。結(jié)果顯示一切如我們所預(yù)期一樣運(yùn)行。
總結(jié)
作為全文的結(jié)尾,讓我們一起對上述各個(gè)步驟進(jìn)行一個(gè)鳥瞰式的總結(jié)。
首先,我們開發(fā)的程序并未如我么所預(yù)期那樣運(yùn)行,我們不得不在兩種解決方法中選擇一種來查找程序中的錯(cuò)誤:通過運(yùn)行調(diào)試器或者使用自動錯(cuò)誤檢測技術(shù)。
如果我們使用調(diào)試器運(yùn)行代碼來查找錯(cuò)誤,我們將會看到一些很奇怪的現(xiàn)象:程序中的一些變量總是被賦予了相同的值。基于這種現(xiàn)象我們不得不通過排除法來查找問題的原因——即在應(yīng)該使用比較運(yùn)算符的地方我們錯(cuò)誤地使用了賦值運(yùn)算符。而靜態(tài)代碼分析則能為我們自動地檢查出該邏輯錯(cuò)誤。運(yùn)行時(shí)內(nèi)存分析是不可能檢查出這種錯(cuò)誤的,因?yàn)檫@種錯(cuò)誤與內(nèi)存無關(guān)。數(shù)據(jù)流分析也很有可能找不到這類錯(cuò)誤因?yàn)閿?shù)據(jù)流分析僅僅是通過這些路徑而不會驗(yàn)證這些條件的正確性。
當(dāng)我們解決了這個(gè)問題后,程序可以運(yùn)行了,但是仍然還有內(nèi)存相關(guān)的問題。內(nèi)存相關(guān)的問題是很難被調(diào)試器發(fā)現(xiàn)的;當(dāng)用戶使用調(diào)試器調(diào)試程序時(shí),用戶并不知道內(nèi)存的實(shí)際大小。但是自動錯(cuò)誤檢查工具能夠做到這點(diǎn)。因此,為了查找這些內(nèi)存問題,我們將整個(gè)程序進(jìn)行插裝,并使用運(yùn)行時(shí)內(nèi)存分析工具來運(yùn)行程序。這樣我們就能知道到底是那一片內(nèi)存發(fā)生了寫溢出錯(cuò)誤。
盡管如此,在審查覆蓋率分析結(jié)果的時(shí)候,我們注意到在目標(biāo)板上測試的時(shí)候,并不是全部代碼都被覆蓋到了。通過自動化的工具得到這樣的覆蓋率信息是簡單的,因?yàn)楣ぞ邥詣拥馗櫢采w率,但是,如果我們是通過調(diào)試器,就不得不判斷哪一部分程序經(jīng)過了驗(yàn)證。而這通常只能依靠我們?nèi)斯び涗浀姆绞絹韺?shí)現(xiàn)。
當(dāng)工具提醒我們一些代碼未被覆蓋到時(shí),我們決定改變單元測試來額外地增加我們測試執(zhí)行的覆蓋率。這就揭示了程序中另外一些問題。在目標(biāo)系統(tǒng)的正常測試中,覆蓋所有函數(shù)也許是不可能完成的任務(wù),因?yàn)槠渲幸恍┖瘮?shù)可能是硬件的失敗處理函數(shù)或僅在某些小概率的特定情況下才會被調(diào)用的函數(shù)。而對這些函數(shù)的測試對于一些注重安全性的程序而言又是至關(guān)重要的。試想在飛機(jī)上用來處理速度傳感器問題的程序中存在著代碼錯(cuò)誤:我們會有系統(tǒng)崩潰的危險(xiǎn),而不是導(dǎo)致某個(gè)設(shè)備為非工作狀態(tài)。因此,通過創(chuàng)建單元測試用例來覆蓋這類型的執(zhí)行路徑往往是對其進(jìn)行有效測試的唯一方法。
接下來,我們修復(fù)了工具檢查到的所有問題,同時(shí)通過驗(yàn)證相應(yīng)的結(jié)果創(chuàng)建了一個(gè)回歸測試用例(作為報(bào)告的任務(wù)之一引導(dǎo)我們完成)。然后我們運(yùn)行數(shù)據(jù)流分析來覆蓋在目標(biāo)系統(tǒng)上即便使用單元測試也未執(zhí)行到的路徑。在此之前,我們幾乎已經(jīng)達(dá)到了100%的代碼行覆蓋率,但是我們的路徑覆蓋率卻未達(dá)到這個(gè)水平。BugDetective幫我們發(fā)現(xiàn)了這些方面的一些潛在問題。這些問題可能并沒有實(shí)際發(fā)生或者有可能永遠(yuǎn)不會發(fā)生。也許在實(shí)際運(yùn)行時(shí),這些問題僅僅會在當(dāng)其條件滿足的情況下才會出現(xiàn),并且在現(xiàn)實(shí)生活中,這些條件可能永遠(yuǎn)不可能滿足。盡管如此,我們不能保證隨著代碼的升級,應(yīng)用程序不會執(zhí)行到這些路徑。
安全起見,我們?nèi)匀恍薷牧怂鶊?bào)告的問題以排除任何可能影響它的實(shí)際應(yīng)用執(zhí)行的風(fēng)險(xiǎn)。在修改代碼的同時(shí),我們同時(shí)也引入了回歸測試,當(dāng)我們再次運(yùn)行單元測試時(shí)立即被檢測到。在所有的自動化錯(cuò)誤檢測方法中,回歸測試是唯一能夠幫助我們檢查到代碼是否發(fā)生了功能性的改變的方法,并且能驗(yàn)證出對代碼進(jìn)行的修改是否引入了功能性的錯(cuò)誤以及不可預(yù)知的副作用。最后,我們修改了回歸測試套件,并重新測試代碼,發(fā)現(xiàn)一切運(yùn)行正常。
正如讀者所見,我們使用的一切測試方法——基于模式的靜態(tài)代碼分析、內(nèi)存分析、單元測試、數(shù)據(jù)流分析以及回歸測試——并不是相互競爭的關(guān)系,恰好相反,它們是一種互補(bǔ)的關(guān)系。將上述工具結(jié)合使用,它們就是一套具有強(qiáng)大作用的工具集,并為嵌入式C語言程序/軟件提供一個(gè)無可比擬的自動化錯(cuò)誤檢測解決方案。
總而言之,通過自動地查找很多關(guān)于內(nèi)存和其它編碼的缺陷,我們成功地讓程序運(yùn)行起來了。盡管如此,值得注意的是,最危險(xiǎn)的缺陷卻是實(shí)際的功能性錯(cuò)誤:例如程序并未如所指定的要求運(yùn)行。而不幸的是,這些錯(cuò)誤往往是非常難以被發(fā)現(xiàn)的。
查找這類缺陷的最好的一個(gè)方式就是通過同行代碼審查來實(shí)現(xiàn)。即另指派至少一人來檢查代碼并且審查代碼與需求內(nèi)容的一致性,這樣用戶就能對實(shí)際程序是否會如預(yù)期那樣運(yùn)行有一個(gè)很好的*估。
另外一個(gè)十分有用的策略是圍繞代碼創(chuàng)建一個(gè)回歸測試套件,這能幫助用戶快捷地驗(yàn)證代碼與規(guī)范的一致性。在本文所描述的示例情景中,單元測試被用來強(qiáng)制執(zhí)行應(yīng)用程序級的運(yùn)行時(shí)內(nèi)存監(jiān)測所未覆蓋到的代碼:它能覆蓋到當(dāng)前程序的功能性,在此之后,我們對代碼做了一些修改,它能提醒我們代碼出現(xiàn)的相應(yīng)的功能性問題。事實(shí)上,這種單元測試用例應(yīng)該被更早地創(chuàng)建起來:理想情況下,當(dāng)用戶在實(shí)現(xiàn)程序的功能時(shí)就應(yīng)該被創(chuàng)建起來。這樣,用戶就能得到更高的覆蓋率并同時(shí)構(gòu)建起一個(gè)更強(qiáng)壯的“安全網(wǎng)”來捕捉關(guān)鍵的功能性改變。
Parasoft的C++test能幫助用戶完成這兩個(gè)任務(wù):從自動化到管理同行代碼審查流程,以及幫助團(tuán)隊(duì)創(chuàng)建,持續(xù)地運(yùn)行并維護(hù)一個(gè)高效的回歸測試套件。
關(guān)于Parasoft C++test
Parasoft C++test是一個(gè)經(jīng)廣泛的最佳實(shí)踐證明能提升軟件開發(fā)團(tuán)隊(duì)開發(fā)效率以及軟件質(zhì)量的自動化集成解決方案。C++test能進(jìn)行諸如編碼策略增強(qiáng)、靜態(tài)代碼分析、運(yùn)行時(shí)內(nèi)存監(jiān)測、自動同行代碼審查以及單元和組件測試,從而為軟件開發(fā)團(tuán)隊(duì)提供一種更加實(shí)用的方法來確保其C以及C++程序能如所預(yù)期那樣工作。C++test可以用于在通用開發(fā)IDE下的桌面平臺中,以及在回歸測試時(shí)通過命令行以批處理模式的方式運(yùn)行。同時(shí),C++test還集成了Parasoft的報(bào)告系統(tǒng),該系統(tǒng)能提供具有細(xì)分能力的基于Web 的儀表板,這使得開發(fā)團(tuán)隊(duì)根據(jù)C++test的測試結(jié)果和其他的一些關(guān)鍵進(jìn)程指標(biāo)來更加方便地跟蹤項(xiàng)目的狀態(tài)和趨勢。
通過在宿主機(jī)上進(jìn)行大量的測試以及在目標(biāo)系統(tǒng)中進(jìn)行的平滑的驗(yàn)證,C++test能夠幫助軟件開發(fā)團(tuán)隊(duì)減少花在嵌入式系統(tǒng)開發(fā)中的時(shí)間、精力以及成本。隨著代碼在宿主機(jī)上的構(gòu)建,C++test的自動化框架使得開發(fā)者能在目標(biāo)硬件系統(tǒng)尚未準(zhǔn)備好的情況下就開始測試以提升代碼質(zhì)量。這大大地縮短了花在目標(biāo)系統(tǒng)上測試的時(shí)間。早期在宿主機(jī)上構(gòu)建的測試套件可以被重用來在仿真器或真實(shí)的目標(biāo)板上驗(yàn)證程序的功能性。
評論