先解釋一什麼叫 Static Code Analysis(簡稱SCT好了),顧名思義:不會動的時候分析一下code。也可以看到wikipedia的條目

這通常由自動化的工具來分析(不然,我想Code Review也算吧),主要希望能在source code 階段,發現一些邏輯上或程式相依上可以發現的潛在問題。有人會覺得這件事compiler不就會做了嗎?用gcc -Wall, 用vc /W4,開下去就幫你檢查完了。可是事實上compiler只會幫你檢查syntax是否符合語言的規範,一些邏輯上問題他不會幫你檢查,SCT就是補足這方面的不足。

舉例以C/C++來說(引用自klocwork)
Null Pointer dereference
如果 x = (11 12 13 14 16 17 18 19) ,就爆了

void foo(int* p) {
*p = 32;
}
void bar(int x) {
int* p = NULL;
if( x == 15 || x == 20 )
p = &x;
if( x > 10 && x <= 20 )
foo(p);
}


Buffer Overflow
如果 data 指向的string 大小大於32 ?

void foo(unsigned char* data) {
unsigned char value[32];
int len = (int)data [0];
memcpy(value, data + 1, len);
}


Memory Leak
嗯,多插2G ram

void foo() { malloc(32); }



除了邏輯上的問題,SCT也可以協助檢查style的問題,比如果在constructor請問intial list 來初始化成員,或功能強一的可以檢查是否符合"Effective C++"建議的條目,並提供一些統計數據讓開發者可以對自已的程式碼品值有一定的掌控度。
其實這種tool在早早以前就有,還記當時年紀小連class都不會寫的時候,跑去看圖書館看"C/C++ user journal"(停刊了,哭哭),側邊的廣告很多都是跟SCT相關。這樣的工具甘要錢,大概看了一下一套400us,但open source 界佛心來著,也提供了一套免錢的功能沒那麼強大工具,就是cppcheck
我自已是使用ubuntu,apt-get裝一下就有了,使用方法非常簡單,以我pcmanx-gtk為例,0.3.9為例

#-f 是檢查所有可能條件編譯的路徑
cppcheck --enable=all -f pcmanx-gtk/src


產生出的log大概長這樣

Checking src/appconfig.cpp: USE_DOCKLET...
[src/appconfig.cpp:54]: (style) Member variable not initialized in the constructor 'CAppConfig::ShowTrayIcon'
Checking src/appconfig.cpp: USE_EXTERNAL...
Checking src/appconfig.cpp: USE_MOUSE...
[src/appconfig.cpp:54]: (style) Member variable not initialized in the constructor 'CAppConfig::MouseSupport'
Checking src/appconfig.cpp: USE_PROXY...
Checking src/appconfig.cpp: USE_WGET...
[src/appconfig.cpp:54]: (style) Member variable not initialized in the constructor 'CAppConfig::UseWgetFiles'
Checking src/appconfig.cpp: __GNUG__...
1/41 files checked 2% done
Checking src/autologinpage.cpp...
2/41 files checked 4% done
Checking src/configfile.cpp...
Checking src/configfile.cpp: __GNUG__...
3/41 files checked 7% done


來看看他會檢查出什麼問題,底下是我節錄+整理的
style

[src/appconfig.cpp:54]: (style) Member variable not initialized in the constructor 'CAppConfig::SocketTimeout'
[src/core/caret.cpp:32]: (style) Member variable not initialized in the constructor 'CCaret::m_GC'
[src/core/fileutil.c:29]: (style) The scope of the variable fddest can be reduced
[src/core/termview.cpp:895]: (style) Redundant condition. It is safe to deallocate a NULL pointer


possible error

[src/editfavdlg.cpp:147]: (possible error) Memory leak: dlg
[src/editfavdlg.h:50]: (possible error) Memory leak: CEditFavDlg::m_List
[src/nancy_bot/msgdata.cpp:345]: (possible error) Memory leak: vsm


分析的結果只代表有修正的可能,不代表一定有錯,以memory leak 那項來說,只有2,3是真的有問題,第1個是因為gui的程式通常把child widget的釋放交給parent去做,所以不會有free的動作,還不賴吧。
除了cppcheck,也有另一套針對C語言的SCT open source 工具 splint,但是我搞不定他的設定,有人搞定可以教我一下嘛。另外最近事業做很大的clang/llvm,也提供一套C/C++/object-c的SCT工具,裝是裝起來了,但是怎麼試都不會有檢測報告,也請用過的人教一下吧
創作者介紹

軟趴趴

cmchao 發表在 痞客邦 PIXNET 留言(4) 人氣()


留言列表 (4)

發表留言
  • 青蛙公主~
  • 上來逛逛,既來之,打聲招呼,留言支持一下囉!祝你一切平安!
  • 路過
  • 感謝您的介紹,看了這篇才知道有 cppcheck 這個好東西,謝謝!

    類似文章:http://stackoverflow.com/questions/141498/what-open-source-c-static-analysis-tools-are-available

    ==
    Splint 剛剛稍微試了一下,在此提供 Windows 下的簡單使用流程:

    1. 到官方網站去下載編譯好的版本:
    http://www.splint.org/win32.html
    將其解壓縮到 D 槽。這樣就可以得到主要的執行檔位於:
    D:\splint-3.1.1\bin\splint.exe

    2. 使用 splint 之前,必須先設定環境變數,否則無法正確執行 splint.exe。根據剛剛那個網頁所說,必須要新增三個環境變數。第一個是 LARCH_PATH,把其值設為D:\splint-3.1.1\lib。
    然後第二個是 LCLIMPORTDIR,把其值設為D:\splint-3.1.1\imports。
    最後是新增環境變數 include,我的電腦是 Windows XP + VC9 編譯器,原本也沒有 include 這個環境變數,所以暫時沒設。

    3. 到此網站抓圖形介面的 Split GUI:
    http://crissi.linux-administrator.com/linux/splintgui/index_en.html
    解壓縮到桌面。

    執行裡面的 splint_gui.exe,然後切換到 Option 分頁去,設定 Splint 的位置,也就是 D:\splint-3.1.1\bin\splint.exe,設定完即可使用。

    此分頁畫面往下就是一堆 Splint 選項參數,預設都沒勾選。

    這是說明手冊:
    http://www.splint.org/manual/manual.html
    沒什麼耐心看,所以就不動他們。

    再下面有個 Compiler Options,其中的 Include dirs 就是剛剛提到的環境變數 include。

    如果有個程式碼,裡面有 #include <stdio.h>,而且有自己勾選 preproc,則會出現錯誤訊息說
    Cannot find include file stdio.h on search Path:
    C:/include;C:/local/include

    可以在 Include dirs 加上『-I-IF:\Microsoft Visual Studio 9.0\VC\include』就能找到 stdio.h。

    4. 實際測試。

    在測試程式碼時,會不認得 .cpp 這個副檔名,還不知道如何解決,隨便新增一個 123.c 內容如下:

    int main() {
    int i = 1;
    int arr[2] = {10, 20};
    arr[i] = ++i;
    }

    然後一切按照 Splint GUI 的預設值,選擇此檔案後,按下 check 按鈕即可出現 2筆 警告訊息,總之就是做了未定義的行為,還有說要 return 整數卻沒 return。

    123.c(5,12): Expression has undefined behavior (left operand uses i, modified
    by right operand): arr[i] = ++i
    Code has unspecified behavior. Order of evaluation of function parameters or
    subexpressions is not defined, so if a value is used and modified in
    different places not separated by a sequence point constraining evaluation
    order, then the result of the expression is unspecified. (Use -evalorder to
    inhibit warning)
    123.c(7,2): Path with no return in function declared to return int
    There is a path through a function declared to return a value on which there
    is no return statement. This means the execution may fall through without
    returning a meaningful result to the caller. (Use -noret to inhibit warning)

    暫時還不能體會這軟體的好用,希望大家補充。
  • 這麼冷門的東東還有人看,真是辛苦你了。
    我有試用過splint的,我在ubuntu的環境上直接用command line測試,但是跟你的測試相同,他需要一個與編譯環境一樣的檢測環境
    就是environment, include path, pre-define symobl等等,我測的project比較大,就會搞的很複雜,所以後來就不想試了。
    不過splint的方法是比較合理的,應為本來就應該要檢測你會使用到的外部函式,才能比較正確分析程式上的問題,cppcheck 其實有點偷吃步

    cmchao 於 2010/06/13 03:08 回覆

  • 路過
  • 修正一下上面回文之錯誤:

    >> 可以在 Include dirs 加上『-I-IF:\Microsoft Visual Studio 9.0\VC\

    應該是 『-IF:\Microsoft Visual Studio 9.0\VC\include』才對

    然後,Splint 只支援 C 語言的檢查而已,不支援 C++ 的檢查,之前沒仔細看清楚
  • Visual Studio 有自已的SCT軟體,不過是包在在Visual studio team system development 版裡(這很貴)
    vc2010 不知道有沒有什麼改變就是了

    cmchao 於 2010/06/13 03:13 回覆

  • J
  • 我在eclipse中使用cppcheck套用上述第一則範例
    #include <stdio.h>
    void foo(int* p);
    void bar(int x);

    int main()
    {
    int i;
    for(i = 0; i<20; i++)
    bar(i);
    return 0;
    }

    void foo(int* p) {
    *p = 32;
    }

    void bar(int x) {
    int *p = NULL;
    if( x == 15 || x == 20 )
    p = &x;
    if( x > 10 && x <= 20 )
    foo(p);
    }

    不過沒有抓到錯誤??
找更多相關文章與討論