靜態程式碼分析工具大比拼(C++篇)
1引言
靜態程式碼分析是指無需執行被測程式碼,通過詞法分析、語法分析、控制流、資料流分析等技術對程式程式碼進行掃描,找出程式碼隱藏的錯誤和缺陷,如引數不匹配,有歧義的巢狀語句,錯誤的遞迴,非法計算,可能出現的空指標引用等等。統計證明,在整個軟體開發生命週期中,30% 至 70% 的程式碼邏輯設計和編碼缺陷是可以通過靜態程式碼分析來發現和修復的。
在C++專案開發過程中,因為其為編譯執行語言,語言規則要求較高,開發團隊往往要花費大量的時間和精力發現並修改程式碼缺陷。所以C++ 靜態程式碼分析工具能夠幫助開發人員快速、有效的定位程式碼缺陷並及時糾正這些問題,從而極大地提高軟體可靠性並節省開發成本。
靜態程式碼分析工具的優勢 :
1.自動執行靜態程式碼分析,快速定位程式碼隱藏錯誤和缺陷。
2. 幫助程式碼設計人員更專注於分析和解決程式碼設計缺陷。
3. 減少在程式碼人工檢查上花費的時間,提高軟體可靠性並節省開發成本。
2業界主流靜態程式碼掃描工具概況
目前市場上的C++ 靜態程式碼分析工具種類繁多且各有千秋,本文將分別介紹TSC團隊自主研發的tscancode工具和當前4種主流C++靜態程式碼分析工具(cppcheck、coverity、clang、pclint),並從功能、效率、易用性等方面對它們進行分析和比較,以期幫助 C++開發人員更清晰靜態程式碼分析工具的工作效果、適用場景和擴充套件空間,同時在其對應專案特徵中選擇合適的工具應用到專案開發環節中。
以下為工具在付費價格、規則數量、準確率、掃描效率、編譯依賴、IDE支援、跨平臺支援、可擴充套件開發方面的對比資料。注:本次競品分析的選擇了3款遊戲專案(約500萬行程式碼)。
在可擴充套件性上,TSC有專人維護,定期根據使用者需求擴充套件規則或新增功能特性,cppcheck和clang是開源工具,工具更新較慢,但如果使用者有特殊需求可以自己擴充套件開發,pclint和coverity是商業軟體,難以進行功能擴充套件。
同時,TSC有完整程式碼質量管理閉環平臺QOC支援; coverity和clang可用web端的結果展示,但無法自行管理問題流,需要進行二次開發; cppcheck和pclint
以下重點比較具體檢查規則和有效問題報錯率。
3檢查規則大比拼
3.1規則大類
針對業內大量掃描工具在實際專案中掃描結果的影響比較,我們將程式碼質量問題分為以下幾大類:
① 致命類:可能導致程式宕機、無響應等影響範圍極大的錯誤;
② 邏輯類:可能造成程式不能達到預期邏輯結果的錯誤;
③ 編碼規範及其他類:可能造成程式的可讀性、可維護性較差的錯誤(不可達程式碼,無效的變數宣告等);
3.2 規則大類分佈
根據3大影響分類,其嚴重程度分別為高、中、低,各型別規則數量分佈為:
從規則分類佔比來看:
① TSC針對網際網路產品高效開發修復原則,工具定位為針對致命和邏輯類問題,相對傳統、軍事、安全領域,並不關注編碼規範及編譯錯誤;
② coverity作為商業化軟體,在付費後新增規則上,達到覆蓋率最全面,除致命和邏輯類規則外,還有大量編碼規範、安全和針對其他語言(如java,C#)的規則;
③ cppcheck作為開源工具,應用範圍廣泛,根據開源社群場景蒐集,在各方面都有規則新增,但場景較為粗獷,場景雖多,但有效率不高。例如:cppcheck在初始化檢查上有5個子規則,樣本程式碼共掃描出312個問題,其中有效問題僅8個,有效率僅為3%。
④ pclint作為商業化軟體,在付費後新增規則上,達到覆蓋率最全面,除致命和邏輯類規則外,還有大量編碼規範、安全的規則;
⑤ clang作為開源軟體,規則較少,但規則型別分佈較為均勻,在致命、邏輯類,還有編碼規範、安全類都有規則新增。
3.3規則報錯數量
整體規則數量上:pclint[915]>coverity[515]>cppcheck[245]>clang[74]>TSC[67]
可以看出pclint和coverity規則最多,TSC和clang規則最少,原因有如下3點:
① pclint和coverity作為商業化軟體,需求來源於傳統軟體、軍事、安全各個領域,其規則總數最多,其編碼規範類規則數量分別高達646條和382條;排除掉低價值的編碼規範類規則,規則數量排序為:pclint[269]>cppcheck[151]>coverity[133]>TSC[67]>clang[44]
② 在規則實際報錯數量上,以3款遊戲500萬行程式碼的結果覆蓋度來看;
注:規則總數指工具所有的規則總數,報錯規則數指開啟工具所有規則情況下,掃描樣本程式碼所覆蓋的規則數量。
從實際專案掃描結果來看:
掃描出問題的規則數/規則總數:TSC[60%]>cppcheck[27%]>clang[19%]>coverity[10%]>pclint[9%]
pclint、coverity、cppcheck雖然規則數量很多,但因為其定製加入的大部分規則普遍適用度不高,大量規則可能在多個專案中都無法掃描出問題。有些規則卻在多個專案中掃描出大量非核心的問題,如:函式沒有被呼叫、未使用的變數、存在多餘的標頭檔案等。
③ 規則數量多來源於兩個方面,一方面是規則覆蓋更全面,另一方面是規則粒度劃分得更細;
通過對具體規則進行分析,發現在規則劃分粒度由細到出排序為[pclint,coverity,cppcheck,clang,TSC]
pclint和coverity劃分粒度最細,cppcheck,clang次之,TSC最粗。
例如:coverity的除0報錯分為整型除0,浮點數除0,取模除0;陣列下標越界也細分為訪問越界、讀越界、寫越界。Pclint和cppcheck初始化分為變數未初始化、結構體成員未初始化、類成員未初始化、string未初始化、data未初始化、union未初始化、全域性靜態變數未初始化等;而TSC則合併了一些過細的規則,未初始化上只分為變數未初始化和成員未初始化。
粒度劃分越細既有優點也有缺點:
優點:可以針對細分規則靈活配置開關,關掉準確率低的規則
缺點:規則數量太多, 使用者配置相當麻煩,新使用者很難理解多個相似的規則之前的區別。
TSC為降低使用者配置難度,在規則粒度劃分上相對粗獷,但會從中提取出其中準確率低的場景,作為單獨規則,從而達到可以關掉低準確率規則的目的。
4同類規則效果對比分析
本文針對每個工具在關鍵報錯項,如:空指標、越界、變數未初始化、記憶體洩露、邏輯上的報錯結果進行分析。
樣本程式碼——3款遊戲專案(約500萬行程式碼)程式碼
測試物件——tscancode2.0、coverity7.5、cppcheck1.68、pclint9.0、clang3.4
有效報錯數——某類規則在3款遊戲專案的有效報錯數總和
準確率——某類規則在3款遊戲專案的平均準確率,準確率=有效報錯數/報錯總數*100%
綜合評分——綜合有效報錯數和準確率的評分,有效報錯數和準確率的權值暫定為45:55,綜合評分=有效報錯/最大有效報錯數*100*45%+準確率*100*55%
4.1空指標規則
空指標檢查規則主要檢查是否存在對賦值為空的指標解引用的情況,空指標是c/c++中最大的問題,經常造成程式崩潰的致命錯誤。因此,C++靜態程式碼分析工具對空指標的檢查能力顯得尤為重要。
圖為五個工具對樣本程式碼掃描結果:
從報錯數量和準確率來看:
有效報錯數:TSC [401] >coverity[219]>>clang[57] >cppcheck[20]>pclint[14]
準確率: coverity[95%]≈TSC[92%] ≈clang[90%]>>cppcheck[28%]>pclint[14%]
綜合評分: TSC[96分] >coverity[77分] >clang[56分]>cppcheck[18分]>pclint[8分]
1. 從準確率來看,在空指標檢查方面,不考慮掃描效率和掃描環境搭建複雜度,TSC、coverity和clang都很優秀,三者準確率都很高。cppcheck, pclint在結果準確率上和數量上都較差,不推薦使用。
2. 從空指標規則細分程度來看,TSC和coverity相當,細分場景挖掘更多,cppcheck規則並未細分空指標規則,從實際專案結果來看,只能檢查出dereferenceBeforeCheck場景的錯誤。Clang和pclint在空指標細分上維度跟TSC和coverity不同,比如:它們區分是引數指標解引用還是區域性變數解引用,細分粒度不夠且覆蓋場景較少,其覆蓋場景基本都被TSC和coverity包含。
cppcheck掃描出來的問題存在大量誤報,誤報主要是冗餘的判空,並不會引起實際問題,具體誤報場景如下:
3. 從有效報錯數量上,TSC有效報錯數量更多,細分場景挖掘更多,無疑是掃描空指標最佳選擇;clang覆蓋的場景較少,其有效報錯基本都能被coverity和TSC覆蓋,不過由於其準確率較高且免費,與TSC搭配使用也是不錯的選擇;而coverity雖然覆蓋場景多但因為只會報完全可信的問題,因此會漏掉部分有效報錯,例如:指標變數來源於函式返回值,而函式返回值是否為NULL依賴於使用者輸入,在靜態分析中coverity無法判斷其是否會為NULL,為保證準確率會漏掉該指標報錯。若專案對空指標漏報容忍度較高,且有足夠預算採購商業軟體,可以選擇coverity;而cppcheck和pclint檢查出的有效問題極少並伴隨大量誤報,同上結論,不宜使用。
4. 在易用性上,coverity和clang編譯環境構建複雜,編譯時長增加較多;TSC在易用性上也有一個缺點,即為提高準確率,在個別專案存在一次性配置工作。原因是個別項目存在自定義判空巨集,但由於不依賴編譯,TSC掃描的程式碼可能並不完整,導致個別自定義判空巨集找不到,需要在cfg.ini中配置自定義判空巨集。當然,如果掃描的程式碼完整度同編譯環境,則無此問題。
4.2越界規則
越界一般來講是指陣列下標越界,或者緩衝區讀寫越界。這類錯誤會導致非法記憶體的訪問,引發程式崩潰或者錯誤。
下圖是五個工具對樣本程式碼掃描結果:
注:越界對誤報判定的規則比較嚴格,即使場景識別本身無誤,但是通過程式碼邏輯可以推斷該場景不會越界的也判定為誤報。
例如:
這裡由found變數間接推斷出data[region_index]不會越界,將其判定為誤報。
從報錯數量和準確率來看:
有效報錯數:coverity[98]>>TSC [18]>pclint[16] >cppcheck[6]> clang[4]
準確率: clang[100%] >coverity[80%]>TSC[70%] >cppcheck[67%]>>pclint[2%]
綜合評分:coverity[90分] >TSC[54分]≈clang[55分]>cppcheck[40分]>pclint[1分]
1. 在報錯數量上,coverity在越界檢查上有較大的優勢,因為coverity有較強的符號查詢和場景識別能力,能識別相對複雜的越界場景。其他四個工具同coverity相比還有差距,其中pclint存在大量誤報,表現最差。如:TSC和cppcheck只能識別陣列變數本身越界,但如果是一個指標p指向陣列的第一個元素,通過p[i]訪問時的越界,TSC和cppcheck都無法檢查,而coverity能找到p所指向的陣列定義,得到陣列大小,從而判斷p[i]是否越界。
2. clang越界這塊的準確率雖然最高為100%,但其覆蓋的場景單一(strncpy使用越界報了4條),其報錯都被TSC和coverity覆蓋,數量上和其他工具有較大差距。TSC越界檢查結果要略好於cppcheck,clang和pclint,TSC增加了對變數取值範圍的推斷,檢測出是否存在越界的風險。比如:
(TSC越界有效報錯場景)
對於陣列下標iCountry的判定存在風險,程式碼執行到當前上下文時,iCountry可能取值為MAX_QT_COUNTRY_JIFEN_ITEM_CNT,而這正是陣列m_astDataInDB的長度,也就是說在這種邊界情況下會造成了陣列訪問越界。對於如上場景,應該將程式碼修改為iCountry>= MAX_QT_COUNTRY_JIFEN_ITEM_CNT。
4.3變數未初始化規則
變數未初始化顧名思義:變數聲明後沒有賦初值,其分配的記憶體值是隨機的。這也是程式碼中容易出現的問題,會導致不確定的程式行為,造成嚴重的後果。
下圖是五個工具對樣本程式碼掃描結果:
注:結果排除了3個工具都有的檢查項——建構函式中是否存在未初始化成員變數。在實際專案中發現,C++類建構函式中對成員變數不做初始化的情況是普遍的,很多程式碼會採用“延遲初始化”,即在實際用到該物件的時候呼叫類似Initialize的方法進行初始化。因此在此次對比中並沒有把這條規則納入進來。
從報錯數量和準確率來看:
有效報錯數:coverity[75]>>pclint[25] >TSC [9]>cppcheck[8]> clang[1]
準確率: TSC[75%] >coverity[68%]>pclint[26%] > clang[17%] >cppcheck[3%]
綜合評分:coverity[82分] > TSC[47分] >pclint[30分] > clang[10分] >cppcheck[6分]
1. 在報錯數量上,coverity初始化檢查場景覆蓋比其他四個工具要全,TSC為保持準確率,規則覆蓋上比較保守,而cppcheck存在比較嚴重的誤報問題,準確率僅為3%。pclint的誤報也相對很高,clang在初始化這塊顯得無能為力。從上圖可以很容易發現cppcheck的誤報數量相當得高,cppcheck會將如下的場景判定為未初始化:
(cppcheck誤報場景)
SMD_POS是一個簡單的結構體,它包含了一個空的建構函式,cppcheck依據這點判定這是一個未初始化的錯誤。但這樣的場景不會有什麼問題,算是一個誤報。這導致了cppcheck在未初始化規則的結果可信度大大降低。
2. coverity在未初始化這塊的場景覆蓋比較全,特別是對結構體物件的欄位的初始化情況的檢測,因為其基於編譯可對變數做路徑跟蹤,例如:建構函式裡面呼叫了init()函式,coverity會繼續跟蹤init()函式中是否有對變數的賦值,所以掃描覆蓋場景最全。coverity的誤報主要分為兩類:一類是對幾種未初始化場景的識別上存在問題,如:,變數在某個分支的確沒有初始化,但用了一個狀態標識其未初始化,當使用這個變數前會使用狀態標記來判斷其是否沒有初始化,保證使用的變數都是初始化過了的。另一類就是上面提到的“低價值報錯”,即通過程式碼邏輯或者做了程式碼保護,保證變數不會因為沒有初始化而產生實際的問題。如:一個表示時間的結構體,裡面欄位有year,month,day,hour,min,day這個欄位沒有初始化,但實際程式碼中也沒有用到這個欄位,因此並不會產生任何問題。
TSC在未初始化變數的檢查因不具備路徑分析能力,而以分支作用域檢查特定變數在各個程式碼分支的初始化情況,誤報率保持在相對低的一個水平。但場景覆蓋較少,沒有針對結構體欄位的初始化場景做覆蓋。因為對結構欄位的初始化方式相對比較多樣:逐個欄位初始化,函式呼叫初始化,建構函式初始化等。
4.4記憶體/資源洩露規則
記憶體洩漏指由於疏忽或錯誤造成程式未能釋放已經不再使用的記憶體,從而造成了記憶體浪費的情況。記憶體洩漏是靜態下很難檢測的一種錯誤,一般需要動態分析工具進行檢測,如valgrind工具會捕獲malloc()/free()/new/delete的呼叫,監控記憶體分配和釋放,從動態上檢測程式是否存在記憶體洩漏。因此,靜態程式碼分析能檢查的記憶體洩漏就非常有限了,當前各工具主要是從程式碼寫法上檢查記憶體分配和釋放是否配對使用。比如:fopen開啟檔案後在退出函式前是否有執行fclose,new[]和delete[]是否配對使用等。
下圖是五個工具對樣本程式碼掃描結果:
注:以上資料排除了cppcheck35個低價值報錯,這裡排除的cppcheck35個報錯都是基本資料型別的new和delete不匹配(如char* p=new char[100];delete p;)雖然這種寫法不規範,但由於實際上不會造成記憶體洩漏,很多專案不會對此進行修復。
從報錯數量和準確率來看:
有效報錯數:pclint[55] >TSC[40]>coverity [29]>cppcheck[28]> clang[0]
準確率: coverity[100%]=cppcheck[100%] >TSC[73%]>pclint[23%] > clang[N/A]
綜合評分:coverity[79分] ≈ TSC [73分]≈cppcheck[77分]>pclint[57分]>clang[0分]
從報錯數量上看出,在記憶體洩漏檢查方面,pclint雖然發現有效問題最多,但誤報很高,不推薦使用。TSC的有效錯誤數比coverity和cppcheck多,但誤報也相對較高。clang則不具備洩露類場景的檢測能力。
注:由於靜態掃描能檢查的記憶體洩露場景都非常明確,因此一般都不會出現問題,TSC的15個誤報也非場景識別有誤而是工具底層bug導致,後續會對底層bug進行修復。如:#ifdef 和#else分支中各有一個fopen,實際編譯時只會走其中1個分支識別1次fopen,但由於底層bug識別了2次fopen,導致誤報。
4.5邏輯錯誤規則
邏輯錯誤:指可能存在的邏輯問題,如if不同分支內容相同,在switch內缺少break等,對指標使用sizeof進行空間分配等問題。
相關推薦
靜態程式碼分析工具大比拼(C++篇)
1引言 靜態程式碼分析是指無需執行被測程式碼,通過詞法分析、語法分析、控制流、資料流分析等技術對程式程式碼進行掃描,找出程式碼隱藏的錯誤和缺陷,如引數不匹配,有歧義的巢狀語句,錯誤的遞迴,非法計算,可能出現的空指標引用等等。統計證明,在整個軟體開發生命週期中,30% 至
程式碼規範工具大比拼---Alibaba Java Coding Guidelines
程式碼規範工具大比拼---Alibaba Java Coding Guidelines &n
靜態程式碼分析工具清單:開源篇(各語言)
本文是一個靜態程式碼分析工具的清單,共有26個工具。包括4個.NET工具、2個Ada工具、7個C++工具、4個Java工具、2個JavaScript工具、1個Opa工具、2個Packaging工具、3個Perl工具、1個Python工具。 1.NET .NET Compiler Platfo
靜態程式碼分析工具列表分析---程式碼分析工具列表(30款工具)
本文是一個靜態程式碼分析工具的清單,共有30個工具。包括4個.NET工具、2個Ada工具、7個C++工具、4個Java工具、2個JavaScript工具、1個Opa工具、2個Packaging工具、3個Perl工具、1個Python工具、1個嵌入式工具、2個二進位制工具
2017年前端框架、類庫、工具大比拼
and types 測試結果 uga 分布 aaa mage ken pic 相比於JavaScript開發人員的數量,目前JavaScript框架、類庫和工具的數量似乎更多一些。截至2017年5月,GitHub上的快速搜索顯示,有超過110萬個JavaScript項目。n
PVS-Studio C/C++/C++11 靜態程式碼分析工具
靜態程式碼分析儀是一種檢測程式碼缺陷、分析對比 商業程式碼的工具,它分析原始碼和它生成的目標 檔案,但並不實際執行原始碼。應用於對安全性、 穩定性要求很高的領域,比如航天、國防、工業 控制、金融等就需要嚴格的程式碼分析工具。PVS-Studio 能發現程式碼中一些潛
PHP工具箱:PHPStan —— PHP 靜態程式碼分析工具
PHPStan:無需寫測試就能找到程式碼中的 Bug 每當我看到開發人員從 Java 或 C# 等編譯語言切換到 PHP 這樣的解釋語言時解放了生產力後感到很高興。除了這些常規的執行模型(發起、處理請求和結束請求)和更短的反饋環(無需等待編譯器)外,還有一個能解決開發人員日常問題的開源框架
PVS-Studio C/C++/C++11 靜態程式碼分析工具
靜態程式碼分析儀是一種檢測程式碼缺陷、分析對比 商業程式碼的工具,它分析原始碼和它生成的目標 檔案,但並不實際執行原始碼。應用於對安全性、 穩定性要求很高的領域,比如航天、國防、工業 控制、金融等就需要嚴格的程式碼分析工具。PVS-Studio 能發現程式碼中一些潛在
Cppcheck 1 54 C/C++靜態程式碼分析工具
64-bit portabilityCheck if there is 64-bit portability issues:assign address to/from int/longAuto VariablesA pointer to a variable is only valid as long as
UI設計圖的標註工具大比拼
UI設計圖標註工具大比拼 作為一名在乎設計的前端程式,電腦裡備著諸如PS啊AI啊Sketch之類的是一件很平常的事情,方便與設計稿進行對比啊(方便偷偷修改下設計稿,對個畫素改個尺寸什麼的,哈哈- -哈- -……) 當然,這些不是今天主要想說,今天主要想結合自己實際工作
web程式設計速度大比拼(nodejs go python)(非專業對比)
C10K問題的解決,湧現出一大批新框架,或者新語言,那麼問題來了:到底誰最快呢?非專業程式猿來個非專業對比。 比較程式:輸出Hello World! 測試程式:siege –c 100 –r 100 –b 例子包括: 1.go用http模組實現的helloworld 2.go用martini微框架實現的He
【JSHint解讀一】JavaScript的靜態程式碼分析工具
簡介 JSHint是一個社群驅動用來檢測JavaScript程式碼中錯誤和潛在的問題在和執行團隊的編碼規範工具。它非常靈活,因此可以很容易地適應您特定的編碼規則和你的程式碼執行環境。JSHint將永遠保持開源的方式。 目標 這個專案的目標是幫助Java
流行Java IDE工具大比拼
這是一款非常優秀的商業Java IDE開發工具,非常適合做J2EE開發,我目前一直在用,當前最新版本為6.02,我還在用5.12。Idea有官方釋出版已經集成了ANT、JS、Tomcat、WebLogic、CVS、SVN客戶端、JSP/HTML/xml等外掛,支援對JSP/HTML/JS/JAVA程式
常用 Java 靜態程式碼分析工具的分析與比較
簡介: 本文首先介紹了靜態程式碼分析的基本概念及主要技術,隨後分別介紹了現有 4 種主流 Java 靜態程式碼分析工具
大資料學習之小白如何學大資料?(詳細篇)
大資料這個話題熱度一直高居不下,不僅是國家政策的扶持,也是科技順應時代的發展。想要學習大資料,我們該怎麼做呢?大資料學習路線是什麼?先帶大家瞭解一下大資料的特徵以及發展方向。 大資料的三個發展方向,平臺搭建/優化/運維/監控、大資料開發/設計/架構、資料分析/挖掘。 先說一下大資料的4V特徵: 資料
關於在本地idea當中提交spark程式碼到遠端的錯誤總結(第二篇)
當代碼能正常提交到spark叢集執行的時候,出現下面的錯誤: Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Me
【演算法與資料結構】在n個數中取第k大的數(基礎篇)
題目介紹 在n個數中取第k大的數(基礎篇),之所以叫基礎篇是因為還有很多更高階的演算法,這些以後再討論。本文用兩種最基本的方法來解決這個問題。使用java語言描述。例子是十個數中取第三大的。 演算法
機器學習競賽分享:NFL大資料碗(上篇)
kaggle競賽分享:NFL大資料碗 - 上 競賽簡介 一年一度的NFL大資料碗,今年的預測目標是通過兩隊球員的靜態資料,預測該次進攻推進的碼數,並轉換為該概率分佈; 競賽連結 https://www.kaggle.com/c/nfl-big-data-bowl-2020 專案連結,該專案程式碼已經publi
android與C# WebService基於ksoap通信(C#篇)
ldo art fadein length col scripts append hid ldoc 1.打開VS 2013新建項目>>ASP.NET空WEB應用程序(我用的是.net 4.0) 2.在剛建立的項目上加入新建項(Web
小積累(C#篇)
頁面 lag etc typeof session write 積累 常用 query 一.反射 1.GetMethods()返回當前類型的所有公共方法 註:當類型不是Public時 MethodInfo[ ] methods=typeof(program)(Bindin