1. 程式人生 > >利用工具定位記憶體洩漏問題 valgrind memwatch dmalloc

利用工具定位記憶體洩漏問題 valgrind memwatch dmalloc

記憶體洩漏定位工具

記憶體debug有比較多的方法,首先可以參看如下的wiki,檢視大概都有哪些方式,再根據其有缺點選用,適合自己需要的方式。

Memory Debuggers

https://elinux.org/Memory_Debuggers#mpatrol

1 mtrace

2 memwatch

3 mpatrol

4 dmalloc

5 dbgmem

6 valgrind

7 Electric Fence

Memory Leak Detection in Embedded Systems

https://www.linuxjournal.com/article/6059

對於這些記憶體定位的詳細介紹資訊,請參考如上的連結。

對於嵌入式軟體來說,空間要求比較緊張,應用mtrace memwatch dmalloc會比較好一些,valgrind 太大了,很難編譯到嵌入式軟體中去。

對於這些工具的使用,可以看開源軟體本身提供的文件,以下分別介紹,內容也都演化或來自官網的英文文件。更為詳細的可以參考原文件。

  1. Valgrind

Valgrind 是一款 Linux下(支援 x86、x86_64和ppc32)程式的記憶體除錯工具,它可以對編譯後的二進位制程式進行記憶體使用監測(C語言中的malloc和free,以及C++中的new和delete),找出記憶體洩漏問題。

Valgrind 中包含的 Memcheck 工具可以檢查以下的程式錯誤:

使用未初始化的記憶體 (Use of uninitialised memory)

  使用已經釋放了的記憶體 (Reading/writing memory after it has been free’d)

  使用超過malloc分配的記憶體空間(Reading/writing off the end of malloc’d blocks)

  對堆疊的非法訪問 (Reading/writing inappropriate areas on the stack)

  申請的空間是否有釋放 (Memory leaks – where pointers to malloc’d blocks are lost forever)

  malloc/free/new/delete申請和釋放記憶體的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])

  src和dst的重疊(Overlapping src and dst pointers in memcpy() and related functions)

  重複free

網址http://valgrind.org/

    1. 下載Valgrind

Valgrind需要下載來自行根據自己的硬體,編譯出符合本機需求的二進位制檔案。

參考這個網址的操作,可以通過git下載程式碼。

如下是在ubuntu的虛擬機器中,執行的git命令,下載程式碼。

$ git clone git://sourceware.org/git/valgrind.git

Cloning into 'valgrind'...

remote: Counting objects: 123703, done.

remote: Compressing objects: 100% (25117/25117), done.

remote: Total 123703 (delta 94220), reused 123034 (delta 93619)

Receiving objects: 100% (123703/123703), 38.11 MiB | 33.00 KiB/s, done.

Resolving deltas: 100% (94220/94220), done.

Checking connectivity... done.

Checking out files: 100% (5862/5862), done.

也可以在web上直接滑鼠點選下載程式碼。參考如下的最新版本連結。

http://valgrind.org/downloads/current.html

    1. 安裝Valgrind
      1. 編譯原始碼安裝

cd valgrind   //剛剛下載解壓的目錄

  ./autogen.sh

  ./configure --prefix=...   //make install時的安裝目錄

  make

  make install

如上步驟,我在valgrind3.9.0的版本中進行配置,./configure –prefix=/home/quzhifeng/workspace/valgrind/valgrind-3.9.0_2/valgrind 發生瞭如下錯誤:

configure: error: Valgrind requires glibc version 2.2 - 2.17

解決:換成valgrind的最新版本再次執行,就沒有上邊的錯誤了,可能是我的ubuntu14.04 中的libc 的版本太新了,valgrind3.9.0 需要老一點版本的libc。

安裝目錄配置在了我的編譯目錄裡了。

./configure --prefix=/home/quzhifeng/workspace/valgrind/valgrind/valgrindinstall

//make install完成以後,可以在安裝目錄(上邊prefix指定的目錄)中看到如下三個編譯出來的目錄。

bin  include  lib

      1. Ubuntu安裝命令安裝

Sudo apt-get install valgrind

    1. X86平臺試驗Valgrind
      1. 記憶體洩漏試驗

首先構建一段有問題的程式碼

#include <stdio.h>

#include <stdlib.h>

void main()

{

        char *p = malloc(20);

        sprintf(p, "%s", "test");

        fprintf(stderr, "p:%s/n", p);

}

編譯程式碼:

gcc test.c –o  test

//對編譯出的test檔案進行記憶體洩漏分析

~/workspace/valgrind/valgrind/valgrindinstall/bin/valgrind --tool=memcheck ./test

// 如下執行命令的輸出結果

==3556== HEAP SUMMARY:

==3556==     in use at exit: 20 bytes in 1 blocks

==3556==   total heap usage: 1 allocs, 0 frees, 20 bytes allocated

==3556==

==3556== 20 bytes in 1 blocks are definitely lost in loss record 1 of 1

==3556==    at 0x402A4E8: malloc (vg_replace_malloc.c:299)

==3556==    by 0x8048481: main (in /home/quzhifeng/temp.ctest/test)

==3556==

==3556== LEAK SUMMARY:

==3556==    definitely lost: 20 bytes in 1 blocks

==3556==    indirectly lost: 0 bytes in 0 blocks

==3556==      possibly lost: 0 bytes in 0 blocks

==3556==    still reachable: 0 bytes in 0 blocks

==3556==         suppressed: 0 bytes in 0 blocks

分析:看出有一個20個位元組的記憶體申請,但是沒有被free。從上邊你的HEAP SUMMARY 和LEAK SUMMARY中都可以看出。

      1. free一個未malloc的指標

編寫如下程式碼,編譯並用valgrind執行。

void main()

{

        char p[] = "hello";

        fprintf(stderr, "p:%s/n", p);

        free(p);

}

並沒有分配記憶體,確進行了free的操作。

//執行命令:

~/workspace/valgrind/valgrind/valgrindinstall/bin/valgrind --leak-check=full ./test2

//命令輸出結果:

==3592== Invalid free() / delete / delete[] / realloc()

==3592==    at 0x402B555: free (vg_replace_malloc.c:530)

==3592==    by 0x8048509: main (in /home/quzhifeng/temp.ctest/test2)

==3592==  Address 0xbe82f4e6 is on thread 1's stack

==3592==  in frame #1, created by main (???:)

==3592==

==3592==

==3592== HEAP SUMMARY:

==3592==     in use at exit: 0 bytes in 0 blocks

==3592==   total heap usage: 0 allocs, 1 frees, 0 bytes allocated

==3592==

==3592== All heap blocks were freed -- no leaks are possible

==3592==

==3592== For counts of detected and suppressed errors, rerun with: -v

==3592== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

分析:從以上的輸出中可以明顯的看出,有0次分配,一次free的提示資訊。

      1. 棧空間越界讀取

//建立測試程式碼

#include <stdio.h>

#include <stdlib.h>

void main()

{

        char p[8] = "hello"; //p在棧上, "hello"在常量區

        fprintf(stderr, "p10:%c/n", p[100]);

}

//執行檢測

~/workspace/valgrind/valgrind/valgrindinstall/bin/valgrind --leak-check=full ./test3

//測試輸出結果:

==3633== Command: ./test3

==3633==

==3633== Syscall param write(buf) points to uninitialised byte(s)

==3633==    at 0x4128593: __write_nocancel (syscall-template.S:81)

==3633==    by 0x40BC4C0: [email protected]@GLIBC_2.1 (fileops.c:1261)

==3633==    by 0x40BB6FE: new_do_write (fileops.c:538)

==3633==    by 0x40BCB71: [email protected]@GLIBC_2.1 (fileops.c:1343)

==3633==    by 0x40955A9: buffered_vfprintf (vfprintf.c:2377)

==3633==    by 0x4090874: vfprintf (vfprintf.c:1313)

==3633==    by 0x409A26E: fprintf (fprintf.c:32)

==3633==    by 0x80484E2: main (in /home/quzhifeng/temp.ctest/test3)

==3633==  Address 0xbea05f74 is on thread 1's stack

==3633==  in frame #4, created by buffered_vfprintf (vfprintf.c:2323)

==3633==

p10:/n==3633==

==3633== HEAP SUMMARY:

==3633==     in use at exit: 0 bytes in 0 blocks

==3633==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated

==3633==

==3633== All heap blocks were freed -- no leaks are possible

==3633==

==3633== For counts of detected and suppressed errors, rerun with: -v

==3633== Use --track-origins=yes to see where uninitialised values come from

==3633== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

輸出結果提示,寫了未被初始化的記憶體。

      1. 越界讀堆記憶體

測試程式碼

#include <stdio.h>

#include <stdlib.h>

void main()

{

        char *p = malloc(8);

        fprintf(stderr, "p10:%c/n", p[10]);

        free(p);

}

//輸出資訊

==3665== Invalid read of size 1

==3665==    at 0x80484BD: main (in /home/quzhifeng/temp.ctest/test4)

==3665==  Address 0x41fe032 is 2 bytes after a block of size 8 alloc'd

==3665==    at 0x402A4E8: malloc (vg_replace_malloc.c:299)

==3665==    by 0x80484B1: main (in /home/quzhifeng/temp.ctest/test4)

==3665==

p10:/n==3665==

==3665== HEAP SUMMARY:

==3665==     in use at exit: 0 bytes in 0 blocks

==3665==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated

==3665==

==3665== All heap blocks were freed -- no leaks are possible

==3665==

==3665== For counts of detected and suppressed errors, rerun with: -v

==3665== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

      1. Stack上寫越界

void main()

{

        char p[8] = "hello";

        p[10]='a';

}

*** stack smashing detected ***: ./test5 terminated

==3676==

==3676== Process terminating with default action of signal 6 (SIGABRT)

==3676==    at 0x407B607: raise (raise.c:56)

==3676==    by 0x407EA32: abort (abort.c:89)

==3676==    by 0x40B6772: __libc_message (libc_fatal.c:175)

==3676==    by 0x414917A: __fortify_fail (fortify_fail.c:37)

==3676==    by 0x4149109: __stack_chk_fail (stack_chk_fail.c:28)

==3676==    by 0x8048478: main (in /home/quzhifeng/temp.ctest/test5)

==3676==

==3676== HEAP SUMMARY:

==3676==     in use at exit: 0 bytes in 0 blocks

==3676==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated

==3676==

==3676== All heap blocks were freed -- no leaks are possible

==3676==

==3676== For counts of detected and suppressed errors, rerun with: -v

==3676== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Aborted (core dumped)

      1. Heap寫越界

void main()

{

        char *p = malloc(8);

        p[10]='a';

        free(p);

}

==3686== Invalid write of size 1

==3686==    at 0x804846D: main (in /home/quzhifeng/temp.ctest/test6)

==3686==  Address 0x41fe032 is 2 bytes after a block of size 8 alloc'd

==3686==    at 0x402A4E8: malloc (vg_replace_malloc.c:299)

==3686==    by 0x8048461: main (in /home/quzhifeng/temp.ctest/test6)

==3686==

==3686==

==3686== HEAP SUMMARY:

==3686==     in use at exit: 0 bytes in 0 blocks

==3686==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated

==3686==

==3686== All heap blocks were freed -- no leaks are possible

==3686==

==3686== For counts of detected and suppressed errors, rerun with: -v

==3686== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

    1. valgrind對嵌入式裝置記憶體洩漏問題定位

因為嵌入式上所用的cpu,和x86平臺是不同的,所以需要重新使用嵌入式平臺的交叉編譯工具鏈來重新編譯valgrind,這樣valgrind才能在嵌入式平臺中應用。

步驟1,下載程式碼,步驟上面介紹過了,這裡不在贅述。

在下載的程式碼中執行./autogen.sh

步驟2,修改configure 用vim開啟 把armv7*)改成 armv7*|arm)

步驟3,執行如下命令

./configure --host=arm-linux CC=arm-none-linux-gnueabi-gcc CPP=arm-none-linux-gnueabi-cpp CXX=arm-none-linux-gnueabi-g++ --prefix=/opt/valgrind

說明:Prefix後邊的為安裝目錄,執行make install後可執行程式所安裝的目錄。

步驟4,

make

make install

將安裝目錄(/opt/valgrind)中的編譯出來的程式,copy到開發中,可以在開發版中使用該方法定位問題。

注意:--prefix=/opt/Valgrind指定的目錄要與開發板上放置的目錄一致,不然執行valgrind時可能會出現“valgrind: failed to start tool 'memcheck' for platform 'arm-linux': No such file or directory”錯誤。

  1. mtrace

mtrace是一個C函式,在<mcheck.h>裡宣告及定義,函式原型為:

void mtrace(void).

其實mtrace是類似malloc_hook的 malloc handler,只不過mtrace的handler function已由系統為你寫好,但既然如此,系統又怎麼知道你想將malloc/free的記錄寫在哪裡呢?為此,呼叫mtrace()前要先設定 MALLOC_TRACE環境變數:

#include <stdlib.h>

....

setenv("MALLOC_TRACE", "output_file_name", 1);

...

「output_file_name」是儲存檢測結果的檔案的名稱。但是檢測結果的格式是一般人無法理解的,而只要有安裝mtrace的話,就會有一名為mtrace的Perl script,在shell輸入以下指令:mtrace [binary] output_file_name

就會將output_file_name的內容轉化成能被理解的語句,例如「No memory leaks」,「0x12345678 Free 10 was never alloc」諸如此類。

例如以下有一函式:(暫且放下single entry single exit的原則)

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <mcheck.h>

int main() {

char *hello;

setenv("MALLOC_TRACE", "output", 1);

mtrace();

if ((hello = (char *) malloc(sizeof(char))) == NULL) {

perror("Cannot allocate memory.");

return -1;

}

return 0;

}

執行後,再用mtrace 將結果輸出:

- 0x08049670 Free 3 was never alloc'd 0x42029acc

- 0x080496f0 Free 4 was never alloc'd 0x420dc9e9

- 0x08049708 Free 5 was never alloc'd 0x420dc9f1

- 0x08049628 Free 6 was never alloc'd 0x42113a22

- 0x08049640 Free 7 was never alloc'd 0x42113a52

- 0x08049658 Free 8 was never alloc'd 0x42113a96

Memory not freed:

-----------------

Address Size Caller

0x08049a90 0x1 at 0x80483fe

最後一行標明有一個大小為1 byte的記憶體尚未釋放,大概是指「hello」吧。

mtrace的原理是記錄每一對malloc-free的執行,若每一個malloc都有相應的free,則代表沒有記憶體洩露,對於任何非malloc/free情況下所發生的記憶體洩露問題,mtrace並不能找出來。

侷限性:只能用於malloc分配 free釋放的情況,對於用其它的函式分配的記憶體造成的洩漏不適用。

  1. Memwatch

MemWatch由 Johan Lindh 編寫,是一個開放原始碼 C 語言記憶體錯誤檢測工具。MemWatch支援 ANSI C,它提供結果日誌紀錄,能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、記憶體洩漏(unfreed memory)、溢位(Overflow)、下溢(Underflow)等等。

memwatch能檢測未釋放的記憶體、同一段記憶體被釋放多次、位址存取錯誤及不當使用未分配之記憶體區域。請往http://memwatch.sourceforge.net/下載最新版本的Memwatch。

    1. MemWatch的原理
      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-land(NML)記憶體填充。no-mans-land將使用0xFC填充.當no-mans-land開啟時,MemWatch轉變釋放的記憶體為NML填充狀態。

      1. 初始化和結束處理

一般來說,在程式中使用MemWatch的功能,需要手動新增mwInit()進行初始化,並用對應的mwTerm ()進行結束處理。當然,如果沒有手動呼叫mwInit(),MemWatch能自動初始化.如果是這種情形,memwatch會使用atext()註冊mwTerm()用於atexit-queue. 對於使用自動初始化技術有一個告誡;如果你手動呼叫atexit()以進行清理工作,memwatch可能在你的程式結束前就終止。為了安全起見,請顯式使用mwInit()和mwTerm().

涉及的函式主要有:

mwInit() mwTerm() mwAbort()

      1. MemWatch的I/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. MemWatch對C++的支援

可以將MemWatch用於C++,但是不推薦這麼做。請詳細閱讀memwatch.h中關於對C++的支援。

    1. 安裝及使用memwatch

很幸運地,memwatch根本是不需要安裝的,因為它只是一組C程式程式碼,只要在你程式中加入memwatch.h,編譯時加上-DMEMWATCH -DMW_STDIO及memwatch.c就能使用memwatch,例如:

gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test

    1. memwatch輸出結果

memwatch的輸出檔名稱為memwatch.log,而且在程式執行期間,所有錯誤提示都會顯示在stdout上,如果memwatch未能寫入以上檔案,它會嘗試寫入memwatchNN.log,而NN介於01至99之間,若它仍未能寫入memwatchNN.log,則會放棄寫入檔案。

    1. 使用方法
      1. 為自己的程式提供MemWatch功能

在要使用MemWatch的.c檔案中包含標頭檔案"memwatch.h"

使用GCC編譯(注意:不是連結)自己的程式時,加入-DMEMWATCH -DMW_STDIO

如:gcc -DMEMWATCH -DMW_STDIO –o test.o –c   test1.c

      1. 使用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)

2)mwTRACE,mwASSERT,mwVERIFY和mwPuts顧名思義,就不再贅述。僅需要注意的是,Memwatch定義了巨集TRACE, ASSERT 和 VERIFY.如果你已使用同名的巨集,memwatch2.61及更高版本的memwatch不會覆蓋你的定義。MemWatch2.61及以後,定義了mwTRACE, mwASSERT 和 mwVERIFY巨集,這樣,你就能確定使用的是memwatch的巨集定義。2.61版本前的memwatch會覆蓋已存在的同名的TRACE, ASSERT 和 VERIFY定義。

當然,如果你不想使用MemWatch的這幾個巨集定義,可以定義MW_NOTRACE, MW_NOASSERT 和 MW_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,那麼可以在這個函式上設定執行斷點。
其他功能的使用,請參考原始碼的說明。

      1. 分析日誌檔案

日誌檔案memwatch.log中包含的資訊主要有以下幾點:

  • 測試日期
  • 狀態蒐集器的資訊
  • 使用MemWatch的輸出函式或巨集(如TRACE等)的資訊。
  • MemWatch捕獲的錯誤資訊
  • 記憶體使用的全域性資訊統計,包括四點:1)分配了多少次記憶體 2)最大記憶體使用量3)分配的記憶體總量 4)為釋放的記憶體總數
  • MemWatch捕獲的錯誤記錄在日誌檔案中的輸出格式如下:
      1. 注意事項
  • mwInit()和mwTerm()是對應的.所以使用了多少次mwInit(),就需要呼叫多少次mwTerm()用於終止MemWatch.
  • 如果在流程中捕獲了程式的異常中斷,那麼需要呼叫mwAbort()而不是mwTerm()。即使有顯示的呼叫mwTerm(),mwAbort()也將終止MemWatch。

//size="3"

  • MemWatch不能確保是執行緒安全的。如果你碰巧使用Wind32或者你使用了執行緒,作為2.66,是初步支援執行緒的。定義WIN32或者MW_PTHREADS以明確支援執行緒。這會導致一個全域性互斥變數產生,同時當訪問全域性記憶體鏈時,MemWatch會鎖定互斥變數,但這遠不能證明是執行緒安全的。
      1. 結論

從MemWatch的使用可以得知,無法用於核心模組。因為MemWatch自身就使用了應用層的介面,而不是核心介面。但是,對於普通的應用層程式,還是比較有用,並且是開源的,可以自己修改程式碼實現;它能方便地查詢記憶體洩漏,特別是提供的介面函式簡單易懂,學習掌握很容易,對應用層程式的單元測試會較適用。

      1. Memwatch使用注意

Memwatch的優點是無需特別配置,不需安裝便能使用,但缺點是它會拖慢程式的執行速度,尤其是釋放記憶體時它會作大量檢查。但它比mtrace和dmalloc多了 一項功能,就是能模擬系統記憶體不足的情況,使用者只需用mwLimit(long num_of_byte)函式來限制程式的heap memory大小(以byte單位)。

最詳細的使用說明(包括優點缺點,執行原理等)已在README中列出,本人強烈建議各位讀者參考該檔案。

      1. 程式演示

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>

#include <string.h>

#include <stdio.h>

#include "memwatch.h"

int getMem(char **myp)

{

         char *p = NULL;

         p = (char *)malloc(100);

         strcpy(p, "aaaaabbbbb");

         *myp = p;

         return 0;

}

void main()

{

         char *myp = NULL;

         getMem(&myp);

         printf("%s\n", myp);

         system("pause");

         return;

}

編譯及執行

開啟日誌檔案檢視

============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh =============

Started at Wed May 23 19:42:23 2018

Modes: 64-bit mwDWORD==(unsigned long)

mwROUNDALLOC==8 sizeof(mwData)==32 mwDataSize==32

Compiled using Microsoft C 19.00

Stopped at Wed May 23 19:42:25 2018

unfreed: <1> d:\文件\visual studio 2015\projects\記憶體洩漏\記憶體洩漏\記憶體洩漏.c(10), 100 bytes at 03359900  {61 61 61 61 61 62 62 62 62 62 00 FE FE FE FE FE aaaaabbbbb......}

Memory usage statistics (global):

 N)umber of allocations made: 1

 L)argest memory usage      : 100

 T)otal of all alloc() calls: 100

 U)nfreed bytes totals      : 100

  1. dmalloc

dmalloc能夠檢查出直到程式執行結束還沒有釋放的記憶體,並且能夠精確指出在

哪個原始檔的第幾行。 mallocrealloccallocfree 

dmalloc 主頁: http://dmalloc.com

支援的平臺:AIX, BSD/OS, DG/UX, Free/Net/OpenBSD, GNU/Hurd, HPUX, Irix, Linux, MS-DOG, NeXT, OSF, SCO, Solaris, SunOS, Ultrix, Unixware, Windoze, and even Unicos on a Cray T3E

    1. 下載及安裝

到主頁去下載,按下面方式安裝。

  1. tar zxvf dmalloc-5.5.2.tgz
  2. cd dmalloc-5.5.2
  3. ./configure
  4. make
  5. make install
    1. 使用
      1. 設定環境變數:

對於 Bash, ksh, and zsh (http://www.zsh.org/), 在 `.bashrc', `.profile', or `.zshrc'

檔案中加入一行 ( -b 引數表示針對Bash的輸出):

function dmalloc { eval `command dmalloc -b $*`; }

然後重新登陸使用者,或者執行: source ~/.bashrc 或 source ~/.profile

接下面執行:

dmalloc -l logfile -i 100 low

在原始檔中新增下面的C程式碼:

#ifdef DMALLOC

#include "dmalloc.h"

#endif

編譯使用的CFLAGS: -DDMALLOC -DDMALLOC_FUNC_CHECK

如: gcc -DDMALLOC -DDMALLOC_FUNC_CHECK dm_test.c

//====== dm_test.c 原始碼 =============

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#ifdef DMALLOC

#include <dmalloc.h>

#endif

int main(int argc, char **argv)

{

    char *str;

    str = malloc(5);

    str = malloc(6);

    return 0;

}

執行:

./a.out

執行上面的命令會在當前目錄下生成 logfile檔案,檢視logfile的內容如下:

cat logfile

1214894489: 2: Dmalloc version '5.5.2' from 'http://dmalloc.com/'

1214894489: 2: flags = 0x4e48503, logfile 'logfile'

1214894489: 2: interval = 100, addr = 0, seen # = 0, limit = 0

1214894489: 2: starting time = 1214894489

1214894489: 2: process pid = 9560

1214894489: 2: Dumping Chunk Statistics:

1214894489: 2: basic-block 4096 bytes, alignment 8 bytes

1214894489: 2: heap address range: 0xb8020000 to 0xb8029000, 36864 bytes

1214894489: 2:     user blocks: 1 blocks, 4043 bytes (10%)

1214894489: 2:    admin blocks: 8 blocks, 32768 bytes (89%)

1214894489: 2:    total blocks: 9 blocks, 36864 bytes

1214894489: 2: heap checked 1

1214894489: 2: alloc calls: malloc 2, calloc 0, realloc 0, free 0

1214894489: 2: alloc calls: recalloc 0, memalign 0, valloc 0

1214894489: 2: alloc calls: new 0, delete 0

1214894489: 2:   current memory in use: 11 bytes (2 pnts)

1214894489: 2:  total memory allocated: 11 bytes (2 pnts)

1214894489: 2:  max in use at one time: 11 bytes (2 pnts)

1214894489: 2: max alloced with 1 call: 6 bytes

1214894489: 2: max unused memory space: 53 bytes (82%)

1214894489: 2: top 10 allocations:

1214894489: 2:  total-size  count in-use-size  count  source

1214894489: 2:           6      1           6      1  dm_test.c:71

1214894489: 2:           5      1           5      1  dm_test.c:69

1214894489: 2:          11      2          11      2  Total of 2

1214894489: 2: Dumping Not-Freed Pointers Changed Since Start:

1214894489: 2:  not freed: '0xb8028fc8|s1' (6 bytes) from 'dm_test.c:71'

1214894489: 2:  not freed: '0xb8028fe8|s1' (5 bytes) from 'dm_test.c:69'

1214894489: 2:  total-size  count  source

1214894489: 2:           6      1  dm_test.c:71

1214894489: 2:           5      1  dm_test.c:69

1214894489: 2:          11      2  Total of 2

1214894489: 2: ending time = 1214894489, elapsed since start = 0:00:00

那麼,哪個地方的記憶體leak就一目瞭然了。

相關推薦

利用工具定位記憶體洩漏問題 valgrind memwatch dmalloc

記憶體洩漏定位工具 記憶體debug有比較多的方法,首先可以參看如下的wiki,檢視大概都有哪些方式,再根據其有缺點選用,適合自己需要的方式。 Memory Debuggers https://elinux.org/Memory_Debuggers#mpatrol

例項介紹利用valgrind定位記憶體洩漏問題

        在前面的文章中, 我們簡單瞭解了valgrind工具的用途以及安裝, 以便大家能進行實際操作。 在本文中, 我們通過例項來看看如何利用valgrind來定位記憶體洩漏問題。 先看程式: #include <stdio.h> #include &

linux工具之檢測記憶體洩漏-valgrind

0.前言 記憶體洩漏是c++程式常見的問題了,特別是服務類程式,當系統模組過多或者邏輯複雜後,很難通過程式碼看出記憶體洩漏; valgrind是一個開源的,檢測c++程式記憶體洩漏有效工具,編譯時加上-g選項可以定位到程式碼行,同時還檢查‘野指標’,檢查malloc與fre

利用Android中DDMS->Heap工具檢測記憶體洩漏問題

1. 啟動eclipse後,切換到DDMS透檢視,並確認Devices檢視、Heap檢視都是開啟的; 2. 將手機通過USB連結至電腦,連結時需要確認手機是處於“USB除錯”模式,而不是作為“Ma

Andorid效能優化(三) 之 如何定位記憶體洩漏

1 定位記憶體洩漏工具 正所謂工欲善其事,必先利其器。定位記憶體洩漏,可以藉助目前比較流行的一些工具來幫助發現和定位問題,下面我們就來看看這些工具。 1.1 Memory Profiler Android Studio 3.0 採用全新的Android Profiler視窗取代&nb

C++檢測和定位記憶體洩漏的技巧

在實際開發過程中專案中由於各方面原因,總是有人抱怨存在記憶體洩漏,系統長時間執行之後,可用記憶體越來越少,萇至導致了某些服務失敗。記憶體洩漏是最難發現的常見錯誤之一,因為除非用完記憶體或呼叫malloc失敗,否則都不會導致任何問題。實際上,使用C/C++這類沒有垃圾回收機制

Linux中定位記憶體洩漏

1. 什麼是記憶體洩漏 記憶體洩漏是指堆記憶體的洩漏。堆記憶體是指程式從堆中分配的、大小任意的(記憶體塊的大小可以在程式執行期決定)、使用完後必須顯示釋放的記憶體。應用程式一般使用malloc、realloc、new等函式從堆中分配到一塊記憶體,使用完後,程式

linux下檢測和定位記憶體洩漏位置的方法

gtest:http://code.google.com/p/googletest/,可以下載最新的程式碼。下載後,可以參考gtest-1.6.0\make\Makefile寫自己的Makefile。 程式記憶體的資訊(/proc/self/smaps): VMSIZE:

linux記憶體除錯、記憶體洩漏檢測以及效能分析的工具-valgrind

Valgrind這個名字取自北歐神話中英靈殿的入口。 Valgrind的最初作者是Julian Seward,他於2006年由於在開發Valgrind上的工作獲得了第二屆Google-O’Reilly開原始碼獎。 Valgrind遵守GNU通用公共許可證條款,是一款自由軟體。 官

工具valgrind檢測C++程式碼記憶體洩漏

一、valgrind介紹: valgrind是Linux下的一個開源工具,該工具用來檢測c++程式是否有非法使用記憶體的問題,例如訪問了未初始化的記憶體、訪問陣列時越界、忘記釋放動態記憶體等問題。Lin

Unix下C程式記憶體洩漏檢測工具Valgrind安裝與使用

                Valgrind是一款用於記憶體除錯、記憶體洩漏檢測以及效能分析的軟體開發工具。 Valgrind的最初作者是Julian Seward,他於2006年由於在開發Valgrind上的工作獲得了第二屆Google-O'Reilly開原始碼獎。 Valgrind遵守GNU通用公共許

Linux下利用Valgrind工具進行記憶體洩露檢測和效能分析

Valgrind通常用來成分析程式效能及程式中的記憶體洩露錯誤 一 Valgrind工具集簡紹 Valgrind包含下列工具:     1、memcheck:檢查程式中的記憶體問題,如洩漏、越界、非法指標等。     2、callgrind:檢測程式程式碼的執行

例項介紹利用valgrind定位記憶體異常釋放問題(double free 和wrong free)

         之前介紹過利用valgrind來定位記憶體洩漏(慢性病, 會導致程式在某個不確定的時刻異常), 本文我們來簡要介紹利用valgrind來定位記憶體的重複釋放(急性病, 會報紙程式崩潰)。 看程式: #include <stdio.h> #inc

Valgrind記憶體洩漏工具的安裝與使用 -- Linux

Valgrind記憶體洩漏檢測工具是一個十分便捷的工具,可以很快速的檢測出所寫程式是否存在記憶體洩漏現象,這對於C/C++程式設計師顯得尤為重要,因為不論你有多牛逼,也難以保證自己不會忘寫一個delete或者free。一:安裝步驟首先下載一個Valgrind安裝包。1.解壓安裝包 zip格式用 uzip Va

例項介紹利用valgrind定位記憶體非法訪問問題

       本文繼續介紹 valgind的使用, 看程式: #include <stdio.h> int main() { int a[100]; a[10000] = 0; return 0; }       用valgrind分

如何使用Valgrind memcheck工具進行C/C++的記憶體洩漏檢測

1. 使用未初始化的記憶體 Code : #include <stdio.h> #include <stdlib.h> int main(void) { char *p; char c = *p; printf("\n [%c]\n",c);

Linux C/C++ 記憶體洩漏檢測工具Valgrind

下面是一段有問題的C程式程式碼test.c #i nclude <stdlib.h> void f(void) { int* x = malloc(10 * sizeof(int)); x[10] = 0; //問題1: 陣列下標越界 } //問

C/C++的記憶體洩漏檢測工具Valgrind memcheck的使用經歷

 Linux下的Valgrind真是利器啊(不知道Valgrind的請自覺檢視參考文獻(1)(2)),幫我找出了不少C++中的記憶體管理錯誤,前一陣子還在糾結為什麼VS 2013下執行良好的程式到了Linux下用g++編譯執行卻崩潰了,給出一堆彙編程式碼也看不懂。久久不

C++記憶體洩漏檢測工具-Valgrind使用簡介

一  valgrind是什麼? Valgrind是一套Linux下,開放原始碼(GPL V2)的模擬除錯工具的集合。Valgrind由核心(core)以及基於核心的其他除錯工具組成。核心類似於一個框架(framework),它模擬了一個CPU環境,並提供服務給其他工具;

Linux下記憶體洩漏定位常用工具介紹

寫在前面:本人只是一個linux開發新手,對linux下開發充滿熱情,前段時間接手一個linux下程式效能優化的工作,自己在整個過程中零零散散的總結了一些經驗,特別把查詢和定位記憶體洩漏方面的經驗寫下來,尤其是使用的幾個開源工具,希望對別人有用,也等於自己做做筆記,便於以後再次使用或學習。整個過程主要有以下幾