三星成功案例:Parasoft C++test支持編碼規(guī)則有效提高軟件代碼質(zhì)量
編碼規(guī)則幫助您提高代碼質(zhì)量,生成一致代碼,防止易錯編碼風(fēng)格。Robert Buckley對MISRA-C和編碼規(guī)則添加了一個注釋。本文作者JunHo和YoonKyu是三星電子軟件實驗室的工程師。
編碼規(guī)則一致性檢查
為克服嚴(yán)峻的軟件開發(fā)挑戰(zhàn)并同時減少開發(fā)成本,軟件工程領(lǐng)域已經(jīng)形成了自己的規(guī)則慣例,如需求工程分析、設(shè)計技術(shù)、制程開發(fā),等等。許多規(guī)則慣例都應(yīng)用于開發(fā)的實際執(zhí)行階段,如編碼規(guī)則、代碼重構(gòu)、代碼檢查、靜態(tài)分析。其中,編碼規(guī)則是基礎(chǔ),它能夠很好地提高代碼可靠性,幫助不同的開發(fā)人員都生成一致的代碼,并防止易出錯編碼方式的出現(xiàn)。
三星電子重點著眼于通過定義并強(qiáng)制執(zhí)行內(nèi)部編碼規(guī)則來提高代碼質(zhì)量。我們QA部門使用了一個編碼標(biāo)準(zhǔn)一致性檢查器來實現(xiàn)這個目的,但我們并沒有規(guī)范全部地將這個初始檢查工具應(yīng)用到我們的軟件開發(fā)過程中去。因為這個工具功能不強(qiáng),所以我們只是在最后的審核階段才偶爾用一下它。因此我們只看到了它對代碼質(zhì)量的提高起到了一丁點兒的作用。
最近,我們評估了Parasoft的C++TEST,并應(yīng)用它到我們正在進(jìn)行的開發(fā)項目“MOBILE”中。在本文中,我們將從中學(xué)習(xí)到的經(jīng)驗和大家一起分享。在本文中,一個“編碼規(guī)則條目”是指一個在公司編碼規(guī)則和一致性文檔中描述的總的描述條,一個“編碼規(guī)則”(或“規(guī)則”)是指一個在自動化編碼規(guī)則工具中制訂的具體的編碼規(guī)則。
三星電子是一個主要的消費電子公司。盡管三星以主營硬件起家,軟件也迅速成為了我們一個主要的關(guān)注中心,正如大多數(shù)其他的消費電子廠商一樣。這個“MOBILE”項目是三星的一個電子C/C++開發(fā)項目,它是要開發(fā)出一個用于移動設(shè)備的可重用、可擴(kuò)展的面向?qū)ο蟮能浖蚣堋N覀兊?/span>QA部門是一個獨立出來專門測試三星電子開發(fā)出來的軟件的。我們投入了相當(dāng)多的時間來評估自動化工具,以求能最大地減少重復(fù)的工作量。
C/C++編碼標(biāo)準(zhǔn)
這個MOBILE項目使用一套源于總的三星編碼規(guī)則并針對這個項目特別指定的編碼規(guī)則。這套MOBILE項目規(guī)則可以通過改變語言變量和其他開發(fā)約束條件來進(jìn)行改寫。比如,在MOBILE項目中的一些編譯器不支持對異常情況的處理。因此,當(dāng)一個對象的構(gòu)造器被調(diào)用的時候,不可能偵測到資源分配失敗。為解決這個問題,我們在 MOBILE 項目中采用了大家熟知的 two-phase object construction(二階段對象構(gòu)造)技術(shù)(在 MOBILE 項目編碼規(guī)則中有描述):將一個對象的初始化分為對象分配階段和資源分配階段,以通過一個值的方式返回異常情況(見代碼清單 1)。
class ResourceManager { ResourceManager(); // allocate only object result Construct(); // allocate resources // 'result' contains error code }; int main() { // Two phase construction ResourceManager aObject; if (aObject.Construct() == FAIL) printf("Resource allocation is failed"); }
代碼清單 1
另外,這個MOBILE項目需要對編碼規(guī)則進(jìn)行嚴(yán)格恪守。這個項目主要目標(biāo)是建成一個軟件框架給其他開發(fā)人員使用;它必須是一致的、組織良好的,以使得軟件開發(fā)能夠很好地在這個框架上進(jìn)行。越多的項目涉及進(jìn)來,就越需要一個自動化的工具。這就是為什么MOBILE項目要采用一個編碼規(guī)則檢察器。
Parasoft的C++TEST(www.parasoft.com)提供了自動的C/C++單元測試,和自動化的編碼規(guī)則檢查。我們選擇 C++TEST作為我們的編碼規(guī)則檢查器,是因為它對于我們的大多數(shù)考慮來說是最有效的解決方案。
C++TEST 的一個明顯的特點就是它的基于圖形化界面的規(guī)則。如圖 1 顯示了對規(guī)則“每個全程變量必須進(jìn)行初始化”的圖形化界面規(guī)則描述,在分析源代碼時,每當(dāng)發(fā)現(xiàn)一個“全程變量”,這條規(guī)則便會評估邏輯組件。如果以下條件中的任何不相符合,程序就會報告有一個代碼違規(guī):
- 偵測到的全程變量是一個外部的聲明
- 它沒有進(jìn)行初始化
- 它的類型不是一個數(shù)組或類
圖 1: Parasoft C++test中基于圖形化界面的編碼規(guī)則
圖形化界面簡化了規(guī)則創(chuàng)建。大多數(shù)的C++代碼檢查器在創(chuàng)建規(guī)則時需要編寫腳本;這有一定的難度,并要求更多的C++編程知識。
因為現(xiàn)有的條件是圖形化顯示的,所以基本圖形化界面的規(guī)則能被容易地理解和執(zhí)行。通過基于圖形化界面的規(guī)則可以有更好的可擴(kuò)展性,因為通過圖形化界面只有預(yù)定義的節(jié)點和條件可以選擇。
選擇一個編碼規(guī)則檢查器
我們的選擇標(biāo)準(zhǔn)包括產(chǎn)品使用特點,但不是特點的細(xì)節(jié)(規(guī)則選擇、規(guī)則執(zhí)行、可量測性)。我們通過以下途徑來建立這些標(biāo)準(zhǔn):以前的使用編碼規(guī)則檢查器的經(jīng)驗,項目開發(fā)部門的反饋,產(chǎn)品評估報告。
能很靈活地修改規(guī)則:大多數(shù)編碼規(guī)則檢查器包括預(yù)執(zhí)行的規(guī)格。擁有內(nèi)含的規(guī)則可以減少規(guī)則執(zhí)行的工作量。但是這些規(guī)則通常并不完全符合我們的編碼風(fēng)格。而且,因為大多規(guī)則中是簡單地執(zhí)行,不實的報錯是常見的事,因而使得結(jié)果不具可靠性。我們需要一個簡單的方法來自定義規(guī)則以排除異常情況,添加新的規(guī)則或者修改已存在的規(guī)則。類似于LINT的工具可以檢查一些編碼規(guī)則項,但缺少規(guī)則自定義的特點。盡管有些檢查器也能為規(guī)則自定義提供參數(shù)修改,但我需要更多的靈活性來修改詳細(xì)規(guī)則。
能在不同的級別上報告編碼規(guī)則的遵守程度:許多編碼規(guī)則檢查器支持文件級別的報告。然而,出于管理目的,文件包級和項目級的報告成為管理者們所希望有的。比如,要項目經(jīng)理經(jīng)常希望按文件包或項目來瀏覽編碼規(guī)則違規(guī)以編碼規(guī)則違規(guī)的趨勢和對編碼規(guī)則違規(guī)校正的優(yōu)先級別,特別是在項目工期快要到了的時候。
能與開發(fā)環(huán)境相集成:許多規(guī)則違規(guī)能被很容易地校正。比如,像“使用 TAB 而不是空格進(jìn)行縮進(jìn)”之類的違規(guī)可以通過簡單地用TAB替換空格就可以校正。在這樣的情形中,有一個可以直接訪問違規(guī)源代碼(通過與開發(fā)環(huán)境緊密集成)的編碼規(guī)則檢查器,就可以非常有效地減少校正所需要的時間。
另外,當(dāng)一個工程向前推進(jìn)的時候,它將所含更多的文件和更的"include/directive"設(shè)定。如果檢查器不在IDE內(nèi)運行,那么檢查器就要通過導(dǎo)入或同步IDE項目文件(如MAKEFILES,DSP/DSW文件等)來創(chuàng)建工程文件。
能為 C/C++創(chuàng)建統(tǒng)一的規(guī)則:我們的主要的編程語言,C和C++,有一個相似的結(jié)構(gòu)(除了C++具有更多的基于對象的和類屬性編程)。在兩種語言上為相同的項維護(hù)兩種不同的規(guī)則將需要額外的資源。
能夠識別語言變量:相比C,C++的歷史比較短。編譯器提供商們在ISO C/C++發(fā)布以前產(chǎn)出了他們自己人的 C++編譯器,并且研究顯示,許多 C++的實際應(yīng)用并不能很好地支持 C++ ISO 標(biāo)準(zhǔn)(http://www.ddj.com/184405483)。因為編碼規(guī)則檢查器經(jīng)常分析源代碼,所以能識別語言變量就顯得非常重要了。
能檢查未經(jīng)預(yù)編譯的頭文件:一些編碼規(guī)則檢查器缺少對頭文件的直接檢查,取而代之的是,在頭文件中的代碼違規(guī),是通過檢查在預(yù)編譯執(zhí)行的文件中的頭文件進(jìn)行間接地報告。在這種情況下,一些代碼違規(guī)被忽略了,如在頭文件中與預(yù)處理程序指令和注釋相關(guān)代碼違規(guī),這里通常包含一些被其他開發(fā)人員使用的重要信息。所以,直接對頭文件的檢查是一個我們所希望的功能。
使用檢查器的經(jīng)驗
應(yīng)用一個編碼規(guī)則檢查器可以減少在應(yīng)用中的編碼標(biāo)準(zhǔn)違規(guī)。然而,偵測和消除的違規(guī)數(shù)量取決于目標(biāo)工程的特點和編碼規(guī)則的質(zhì)量。我們發(fā)現(xiàn)每個工程都有其相似的整體傾向,但同時又有影響編碼規(guī)則檢查的不同細(xì)節(jié)。因些,得出來的結(jié)果應(yīng)該被看成是一個趨勢而不是一個確定的樣式。
直接的利益是指減少違規(guī)數(shù)量是如何提高代碼質(zhì)量的。
非直接利益是指其他不曾預(yù)料到的帶給開發(fā)人員的好處。
圖 2 顯示了編碼違規(guī)數(shù)量的總體趨勢。為消除規(guī)則不斷發(fā)展帶來的影響,我們使用最新的規(guī)則來進(jìn)行所有的檢查。在一個從10月4日到1月5日間相對穩(wěn)定的違規(guī)數(shù)量之后,在2月5日有一個大約1/8的違規(guī)減少。從2月5日到3月5日間,有一個不希望有的違規(guī)增加,但這是可以接受的,因為目標(biāo)項目仍在繼續(xù)向前推進(jìn)。
圖 2:總的違規(guī)數(shù)量/每千行代碼
圖 3 顯示了違規(guī)校正對只對當(dāng)前模塊有影響的違規(guī)數(shù)量。從采用檢查器之日起,違規(guī)數(shù)量減少了6.6個百分比。這個數(shù)量沒有包括注釋規(guī)則——這通常不會有大的變化影響但確實是需要重要的進(jìn)行變更的工作。
圖 3:小變化違規(guī)數(shù)量/每千行代碼
圖 4 顯示了校正不只影響當(dāng)前模塊的違規(guī)數(shù)量。從引入檢查器以來,違規(guī)減少了19.6個百分點。
圖 4:有大的改變影響的違規(guī)的數(shù)量/每千行代碼
代碼規(guī)則檢查給我們帶來的一個意外收獲,就是它對開發(fā)人員的教育和知識能力提升。新的開發(fā)人員,甚至是一些有經(jīng)驗的開發(fā)人員,在開始的時候不大理解一些編碼規(guī)則項的意義和重要性。比如,編碼規(guī)則項“最好進(jìn)行初始化而不是分配”是被認(rèn)為是一個有效的方法來初始化一個構(gòu)造函數(shù)中的成員變量(可參考:《Effective C++》,作者:Scott Meyers,Addison-Wesley,1992)。一些開發(fā)人員不理解為什么在一個構(gòu)造函數(shù)的列表中對成員進(jìn)行初始化要比在構(gòu)造函數(shù)體中對成員分配初始化值要更好。他們在查看檢查器的檢查結(jié)果時討論這些問題。這就最終幫助開發(fā)人員完全理解了 C++的特點,并且也展示了編碼規(guī)則檢查是如何幫助開發(fā)人員,使他們從犯錯中進(jìn)行學(xué)習(xí)成長的。
另一個間接帶來的好處就是去除了那些不切實際的編碼規(guī)則項。當(dāng)我們應(yīng)用編碼規(guī)則一項目MOBILE中去的時候,我們發(fā)現(xiàn)大多數(shù)開發(fā)人員并不遵守這條規(guī)則即“每行不要80格”,這條規(guī)則的產(chǎn)生是因為有些老的開發(fā)環(huán)境是80格顯示。我們檢查了我們的開發(fā)環(huán)境并得出結(jié)論:在我們的條件下,這種有限制的開發(fā)環(huán)境很少使用,我們建議一行使用更長的格?!彼形覀儗⒁恍械母駭?shù)限制從80格改到150格。這種編碼規(guī)則修改也可以提高開發(fā)人員對遵守編碼規(guī)則的接受程度。
學(xué)到了什么
直到最近以前,我們的檢查還只是QA部門在工程級別上檢查是否遵守編碼規(guī)則。這對于追蹤缺陷傾向和維護(hù)一套規(guī)則是有效果的。但是會有一些缺點。
首先,報告的違規(guī)來源并不總是開發(fā)環(huán)境中的那段代碼。開發(fā)人員通常并不在一個集中的源碼庫中操作,他們在自己的區(qū)域進(jìn)行書寫和修改代碼,然后復(fù)制或check-in源碼到集中的代碼庫中。如果開發(fā)源碼和QA測試的代碼不一樣,那么開發(fā)人員要識別和修改報告違規(guī)的來源就比較困難了。
再者,開發(fā)人員希望立即能夠驗證到對于違規(guī)的校正已經(jīng)消除了存在的問題。
基于這些原因,我們決定要讓開發(fā)人員和QA一樣可以運行編碼規(guī)則檢查工具。
那些不認(rèn)真遵守規(guī)則的開發(fā)人員會產(chǎn)生很多有違規(guī)的代碼,并且也通常不會去校正這些代碼違規(guī)。這是尤其會產(chǎn)生與識別或設(shè)計相關(guān)的違規(guī)問題,這樣會使得在以后的開發(fā)階段來校正這些錯誤非常困難,因為那時進(jìn)行這樣的校正會有一個較大的,整個工程范圍的影響。所以我們推薦將編碼規(guī)則檢查從早期的編碼階段就開始,這樣違規(guī)代碼就能在傳出模塊之前就得到校正。
為什么我們以前的工具沒能在編碼規(guī)則檢查上發(fā)揮效應(yīng)的也是因為在整個組織級別上缺乏對規(guī)則的維護(hù)。
在一套初始規(guī)則建立之后,規(guī)則應(yīng)該得到不斷的修改定義,因為某些規(guī)則也許不適用于特定的項目,開發(fā)風(fēng)格會根據(jù)開發(fā)的領(lǐng)域不同而會有改變。一套規(guī)則應(yīng)在項目不斷往前推進(jìn)的過程中根據(jù)項目的具體操作而進(jìn)行不斷的維護(hù)更新。
自動的編碼規(guī)則檢查是對代碼走查的一個補(bǔ)充。在我們的開發(fā)過程中,代碼走查是強(qiáng)制的,因為這樣可以有效地找出開發(fā)人員的邏輯錯誤和失誤。然而,開發(fā)人員也會因為工期太緊的壓力跳過代碼走查。根據(jù)我們的經(jīng)驗和研究顯示,一些開發(fā)人員很容易被一些編碼風(fēng)格問題弄得很頭痛,并在代碼走查的過程中花很多工夫到這個上面(參見:D. Kelly 和 T. Shepard 編寫的"Qualitative Observations from SoftwareCode Inspection Experiments"一書; CASCON, 2002),從而,一些開發(fā)人員將代碼走查看作是一件耗時卻收效甚微的工作,并不跳過這一步。這種情形可以通過在代碼走查之前使用自動化的代碼規(guī)則檢查來消除代碼違規(guī)的方法來進(jìn)行避免。這樣的話,在代碼走查的時候我們就能將精力集中在發(fā)現(xiàn)邏輯錯誤和嚴(yán)重錯誤上來。而且,自動化的檢查只能涉及到我們編碼規(guī)則項中的大約 50%(一些規(guī)則項用自動化工具具體執(zhí)行起來非常復(fù)雜),所以代碼走查需要用來檢查剩余的規(guī)則項。
小 結(jié)
我們應(yīng)用 C++TEST 到了我們幾個項目中并在每個項目中都取得了很好代碼質(zhì)量提高效果。我們準(zhǔn)備將其應(yīng)用到更多的項目中去,用其分析代碼的質(zhì)量。