内存泄漏定位工具
内存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 太大了,很难编译到嵌入式软件中去。
对于这些工具的使用,可以看开源软件本身提供的文档,以下分别介绍,内容也都演化或来自官网的英文文档。更为详细的可以参考原文档。
- 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/
-
- 下载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
-
- 安装Valgrind
- 编译源代码安装
- 安装Valgrind
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
-
-
- Ubuntu安装命令安装
-
Sudo apt-get install valgrind
-
- X86平台试验Valgrind
- 内存泄漏试验
- X86平台试验Valgrind
首先构建一段有问题的代码
#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中都可以看出。
-
-
- 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的提示信息。
-
-
- 栈空间越界读取
-
//创建测试代码
#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)
输出结果提示,写了未被初始化的内存。
-
-
- 越界读堆内存
-
测试代码
#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)
-
-
- 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)
-
-
- 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)
-
- 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”错误。
- 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释放的情况,对于用其它的函数分配的内存造成的泄漏不适用。
- Memwatch
MemWatch由 Johan Lindh 编写,是一个开放源代码 C 语言内存错误检测工具。MemWatch支持 ANSI C,它提供结果日志纪录,能检测双重释放(double-free)、错误释放(erroneous free)、内存泄漏(unfreed memory)、溢出(Overflow)、下溢(Underflow)等等。
memwatch能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域。请往http://memwatch.sourceforge.net/下载最新版本的Memwatch。
-
- MemWatch的原理
- Memwatch对内存的处理
- 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填充状态。
-
-
- 初始化和结束处理
-
一般来说,在程序中使用MemWatch的功能,需要手动添加mwInit()进行初始化,并用对应的mwTerm ()进行结束处理。当然,如果没有手动调用mwInit(),MemWatch能自动初始化.如果是这种情形,memwatch会使用atext()注册mwTerm()用于atexit-queue. 对于使用自动初始化技术有一个告诫;如果你手动调用atexit()以进行清理工作,memwatch可能在你的程序结束前就终止。为了安全起见,请显式使用mwInit()和mwTerm().
涉及的函数主要有:
mwInit() mwTerm() mwAbort()
-
-
- MemWatch的I/O 操作
-
对于一般的操作,MemWatch创建memwatch.log文件。有时,该文件不能被创建;MemWatch会试图创建memwatNN.log文件,NN在01~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()
-
-
- MemWatch对C++的支持
-
可以将MemWatch用于C++,但是不推荐这么做。请详细阅读memwatch.h中关于对C++的支持。
-
- 安装及使用memwatch
很幸运地,memwatch根本是不需要安装的,因为它只是一组C程序代码,只要在你程序中加入memwatch.h,编译时加上-DMEMWATCH -DMW_STDIO及memwatch.c就能使用memwatch,例如:
gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test
-
- memwatch输出结果
memwatch的输出文件名称为memwatch.log,而且在程序执行期间,所有错误提示都会显示在stdout上,如果memwatch未能写入以上文件,它会尝试写入memwatchNN.log,而NN介于01至99之间,若它仍未能写入memwatchNN.log,则会放弃写入文件。
-
- 使用方法
- 为自己的程序提供MemWatch功能
- 使用方法
在要使用MemWatch的.c文件中包含头文件"memwatch.h"
使用GCC编译(注意:不是链接)自己的程序时,加入-DMEMWATCH -DMW_STDIO
如:gcc -DMEMWATCH -DMW_STDIO –o test.o –c test1.c
-
-
- 使用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都遵照这个规则。
3)ARI机制即程序设置的“Abort, Retry, Ignore选择陷阱。
mwSetAriFunc:
设置“Abort, Retry, Ignore”发生时的MemWatch调用的函数.当这样设置调用的函数地址时,实际的错误消息不会打印出来,但会作为一个参数进行传递。如果参数传递NULL,ARI处理函数会被再次关闭。当ARI处理函数关闭后, meewatch会自动调用有mwSetAriAction()指定的操作。正常情况下,失败的ASSERT() or VERIFY()会中断你的程序。但这可以通过mwSetAriFunc()改变,即通过将函数"int myAriFunc(const char *)"传给它实现。你的程序必须询问用户是否中断,重试或者忽略这个陷阱。返回2用于Abort, 1用于Retry,或者0对于Ignore。注意retry时,会导致表达式重新求值.
MemWatch有个默认的ARI处理器。默认是关闭的,但你能通过调用mwDefaultAri()开启。注意这仍然会中止你的程序除非你定义MEMWATCH_STDIO允许MemWatch使用标准C的I/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,那么可以在这个函数上设置执行断点。
其他功能的使用,请参考源代码的说明。
-
-
- 分析日志文件
-
日志文件memwatch.log中包含的信息主要有以下几点:
- 测试日期
- 状态搜集器的信息
- 使用MemWatch的输出函数或宏(如TRACE等)的信息。
- MemWatch捕获的错误信息
- 内存使用的全局信息统计,包括四点:1)分配了多少次内存 2)最大内存使用量3)分配的内存总量 4)为释放的内存总数
- MemWatch捕获的错误记录在日志文件中的输出格式如下:
-
-
- 注意事项
-
- mwInit()和mwTerm()是对应的.所以使用了多少次mwInit(),就需要调用多少次mwTerm()用于终止MemWatch.
- 如果在流程中捕获了程序的异常中断,那么需要调用mwAbort()而不是mwTerm()。即使有显示的调用mwTerm(),mwAbort()也将终止MemWatch。
//size="3"
- MemWatch不能确保是线程安全的。如果你碰巧使用Wind32或者你使用了线程,作为2.66,是初步支持线程的。定义WIN32或者MW_PTHREADS以明确支持线程。这会导致一个全局互斥变量产生,同时当访问全局内存链时,MemWatch会锁定互斥变量,但这远不能证明是线程安全的。
-
- 结论
-
从MemWatch的使用可以得知,无法用于内核模块。因为MemWatch自身就使用了应用层的接口,而不是内核接口。但是,对于普通的应用层程序,还是比较有用,并且是开源的,可以自己修改代码实现;它能方便地查找内存泄漏,特别是提供的接口函数简单易懂,学习掌握很容易,对应用层程序的单元测试会较适用。
-
-
- Memwatch使用注意
-
Memwatch的优点是无需特別配置,不需安装便能使用,但缺点是它会拖慢程序的运行速度,尤其是释放内存时它会作大量检查。但它比mtrace和dmalloc多了 一项功能,就是能模拟系统内存不足的情況,使用者只需用mwLimit(long num_of_byte)函数来限制程式的heap memory大小(以byte单位)。
最详细的使用说明(包括优点缺点,运行原理等)已在README中列出,本人强烈建议各位读者参考该文件。
-
-
- 程序演示
-
#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
- dmalloc
dmalloc能够检查出直到程序运行结束还没有释放的内存,并且能够精确指出在
哪个源文件的第几行。 malloc
, realloc
, calloc
, free
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
-
- 下载及安装
到主页去下载,按下面方式安装。
- tar zxvf dmalloc-5.5.2.tgz
- cd dmalloc-5.5.2
- ./configure
- make
- make install
-
- 使用
- 设置环境变量:
- 使用
对于 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就一目了然了。