MemWatch的使用

http://www.brantchen.com

     linux下的測試工具真是少之又少,還不好用,近期試用了memwatch,感覺網上的介紹不太好,所以放在這裏跟大家分享 。事實上大部分都是看的幫助,非常多地方翻譯得不好還有錯,請原諒指出最好看原文。假設轉載或引用,請註明我的博客地址,謝謝。

       

1介紹

MemWatch Johan Lindh編寫,是一個開放源碼 C 語言內存錯誤檢測工具。MemWatch支持 ANSI C,它提供結果日誌紀錄,能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、內存泄漏(unfreed memory)、溢出(Overflow)、下溢(Underflow)等等。

 

1.1 MemWatch的內存處理

MemWatch將全部分配的內存用0xFE填充,所以,假設你看到錯誤的數據是用0xFE填充的,那就是你沒有初始化數據。例外是calloc(),它會直接把分配的內存用0填充。

MemWatch將全部已釋放的內存用0xFD填充(zapped with 0xFD).假設你發現你使用的數據是用0xFD填充的,那你就使用的是已釋放的內存。在這樣的情況,注意MemWatch會馬上把一個"釋放了的塊信息"填在釋放了的數據前。這個塊包含關於內存在哪兒釋放的信息,以可讀的文本形式存放,格式爲"FBI<counter>filename(line)"。如:"FBI<267>test.c(12)".使用FBI會減少free()的速度,所以默認是關閉的。使用mwFreeBufferInfo(1)開啓。

爲了幫助跟蹤野指針的寫情況,MemWatch能提供no-mans-landNML)內存填充。no-mans-land將使用0xFC填充.no-mans-land開啓時,MemWatch轉變釋放的內存爲NML填充狀態。

 

1.2初始化和結束處理

一般來說,在程序中使用MemWatch的功能,須要手動加入mwInit()進行初始化,並用相應的mwTerm ()進行結束處理。

當然,假設沒有手動調用mwInit()MemWatch能自己主動初始化.假設是這樣的情形,memwatch會使用atext()註冊mwTerm()用於atexit-queue.對於使用自己主動初始化技術有一個告誡;假設你手動調用atexit()以進行清理工作,memwatch可能在你的程序結束前就終止。爲了安全起見,請顯式使用mwInit()mwTerm().

涉及的函數主要有:

mwInit()   mwTerm()   mwAbort()

 

1.3 MemWatchI/O操作

對於一般的操作,MemWatch創建memwatch.log文件。有時,該文件不能被創建;MemWatch會試圖創建memwatNN.log文件,NN01~99之間。

假設你不能使用日誌,或者不想使用,也沒有問題。僅僅要使用類型爲"void func(int c)"的參數調用mwSetOutFunc(),然後全部的輸出都會按字節定向到該函數.

ASSERT或者VERIFY失敗時,MemWatch也有Abort/Retry/Ignore處理機制。默認的處理機制沒有I/O操作,可是會自己主動中斷程序。你能夠使用不論什麼其它Abort/Retry/Ignore的處理機制,僅僅要以參數"void func(int c)"調用mwSetAriFunc()。後面在1.2使用一節會具體解說。

涉及的函數主要有:

mwTrace()         mwPuts()       mwSetOutFunc() mwSetAriFunc()

mwSetAriAction()   mwAriHandler() mwBreakOut()

 

1.4 MemWatchC++的支持

   能夠將MemWatch用於C++,可是不推薦這麼做。請具體閱讀memwatch.h中關於對C++的支持。

 

2使用

2.1爲自己的程序提供MemWatch功能

Ø       在要使用MemWatch.c文件裏包括頭文件"memwatch.h"

Ø       使用GCC編譯(注意:不是鏈接)自己的程序時,增加-DMEMWATCH -DMW_STDIO
如:gcc -DMEMWATCH -DMW_STDIO –o test.o
–c test1.c

 

2.2使用MemWatch提供的功能

1在程序中經常使用的MemWatch功能有:

Ø        mwTRACE( const char* format_string, ... );
TRACE( const char* format_string, ... );

Ø        mwASSERT( int, const char*, const char*, int )
ASSERT( int, const char*, const char*, int )

Ø        mwVERIFY( int, const char*, const char*, int )
VERIFY( int, const char*, const char*, int )

Ø        mwPuts( const char* text )

Ø        ARI機制(mwSetAriFunc(int (*func)(const char *))
          mwSetAriAction(int action)

          mwAriHandler
( const char* cause )

Ø        mwSetOutFunc(void (*func)(int))

Ø        mwIsReadAddr(const void *p, unsigned len )

Ø        mwIsSafeAddr(void *p, unsigned len )

Ø        mwStatistics( int level )

Ø        mwBreakOut( const char* cause)

 

2mwTRACEmwASSERTmwVERIFYmwPuts顧名思義,就不再贅述。僅須要注意的是,Memwatch定義了宏TRACE,   ASSERT VERIFY.假設你已使用同名的宏,memwatch2.61及更高版本號的memwatch不會覆蓋你的定義。MemWatch2.61及以後,定義了mwTRACE, mwASSERT mwVERIFY宏,這樣,你就能確定使用的是memwatch的宏定義。2.61版本號前的memwatch會覆蓋已存在的同名的TRACE, ASSERT VERIFY定義。

當然,假設你不想使用MemWatch的這幾個宏定義,能夠定義MW_NOTRACE, MW_NOASSERTMW_NOVERIFY宏,這樣MemWatch的宏定義就不起作用了。全部版本號的memwatch都遵照這個規則。

3ARI機制即程序設置的“Abort, Retry, Ignore選擇陷阱。

mwSetAriFunc

設置“Abort, Retry, Ignore”發生時的MemWatch調用的函數.當這樣設置調用的函數地址時,實際的錯誤消息不會打印出來,但會作爲一個參數進行傳遞。

假設參數傳遞NULLARI處理函數會被再次關閉。當ARI處理函數關閉後, meewatch會自己主動調用有mwSetAriAction()指定的操作。

正常情況下,失敗的ASSERT() or VERIFY()會中斷你的程序。但這能夠通過mwSetAriFunc()改變,即通過將函數"int myAriFunc(const char *)"傳給它實現。你的程序必須詢問用戶是否中斷,重試或者忽略這個陷阱。返回2用於Abort 1用於Retry,或者0對於Ignore。注意retry時,會導致表達式又一次求值.

 MemWatch有個默認的ARI處理器。默認是關閉的,但你能通過調用mwDefaultAri()開啓。注意這仍然會中止你的程序除非你定義MEMWATCH_STDIO同意MemWatch使用標準CI/O流。

同一時候,設置ARI函數也會導致MemWatch不將ARI的錯誤信息寫向標準錯誤輸出,錯誤字符串而是作爲'const char *'參數傳遞到ARI函數.

mwSetAriAction

假設沒有ARI處理器被指定,設置默認的ARI返回值。默認是MW_ARI_ABORT

mwAriHandler

這是個標準的ARI處理器,假設你喜歡就雖然用。它將錯誤輸出到標準錯誤輸出,並從標準輸入獲得輸入。

mwSetOutFunc

將輸出轉向調用者給出的函數(參數即函數地址)。參數爲NULL,表示把輸出寫入日誌文件memwatch.log.

mwIsReadAddr:

檢查內存是否有讀取的權限

mwIsSafeAddr:

檢查內存是否有讀、寫的權限

mwStatistics:

設置狀態蒐集器的行爲。相應的參數採用宏定義。

#define MW_STAT_GLOBAL 0      /*僅蒐集全局狀態信息 */

#define MW_STAT_MODULE 1      /*蒐集模塊級的狀態信息 */

#define MW_STAT_LINE   2      /*蒐集代碼行級的狀態信息 */

#define MW_STAT_DEFAULT 0      /*默認狀態設置 */

mwBreakOut:

當某些情況MemWatch認爲中斷(break into)編譯器更好時,就調用這個函數.假設你喜歡使用MemWatch,那麼能夠在這個函數上設置運行斷點。

其它功能的使用,請參考源碼的說明。

 

2.3分析日誌文件

日誌文件memwatch.log中包括的信息主要有下面幾點:

Ø        測試日期

Ø        狀態蒐集器的信息

Ø        使用MemWatch的輸出函數或宏(如TRACE等)的信息。

Ø        MemWatch捕獲的錯誤信息

Ø        內存使用的全局信息統計,包含四點:1)分配了多少次內存 2)最大內存使用量3)分配的內存總量 4)爲釋放的內存總數

MemWatch捕獲的錯誤記錄在日誌文件裏的輸出格式例如以下:

message: <sequence-number> filename(linenumber), information

 

2.4注意事項

mwInit()mwTerm()是相應的.所以使用了多少次mwInit(),就須要調用多少次

mwTerm()用於終止MemWatch.

 

假設在流程中捕獲了程序的異常中斷,那麼須要調用mwAbort()而不是

mwTerm()。即使有顯示的調用mwTerm()mwAbort()也將終止MemWatch

 

MemWatch不能確保是線程安全的。假設你碰巧使用Wind32或者你使用了線程,作爲2.66,是初步支持線程的。定義WIN32或者MW_PTHREADS以明白支持線程。這會導致一個全局相互排斥變量產生,同一時候當訪問全局內存鏈時,MemWatch會鎖定相互排斥變量,但這遠不能證明是線程安全的。

 

3結論

    MemWatch的使用能夠得知,無法用於內核模塊。由於MemWatch自身就使用了應用層的接口,而不是內核接口。可是,對於普通的應用層程序,我覺得還是比較實用,而且是開源的,能夠自己改動代碼實現;它能方便地查找內存泄漏,特別是提供的接口函數簡單易懂,學習掌握非常easy,相應用層程序的單元測試會較適用。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章