利用工具定位内存泄漏问题 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下载代码。

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

如下是在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: _IO_file_write@@GLIBC_2.1 (fileops.c:1261)

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

==3633==    by 0x40BCB71: _IO_file_xsputn@@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就一目了然了。

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值