查找嵌入式C語(yǔ)言程序/軟件中的缺陷的多種技術(shù)
void handleSensorValue(int value)
{
initialize();
int index = -1;
if (value = 10) {
index = VALUE_LOW;
} else {
index = VALUE_HIGH;
}
printMessage(index, value);
}
相同地,我們也對最后一個(gè)報告的錯誤進(jìn)行相應的處理?,F在我們再次運行數據流分析,將不會(huì )再有錯誤被報告出來(lái)。
為了確保程序運行一切正常,我們重新運行整個(gè)分析過(guò)程。首先,我們開(kāi)啟運行時(shí)內存監測并運行應用程序,一切表現正常。然后我們開(kāi)啟內存監測并運行單元測試,一個(gè)任務(wù)被報告出來(lái):

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