内存泄漏定位工具

内存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能够检查出直到程序运行结束还没有释放的内存,并且能够精确指出在

哪个源文件的第几行。 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

  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相关推荐

  1. 用mtrace定位内存泄漏

    一. 缘起 有的公众号读者,看完我上次写给大学生的查bug方法后,希望我多分享一些查bug的实践经验和具体步骤,比如如何查内存泄漏和core dump问题.所以,就打算写这篇文章. 二. 内存泄漏简介 ...

  2. linux如何定位内存泄漏,快速定位内存泄漏的套路(linux)

    快速定位内存泄漏的套路(linux) 快速定位内存泄漏的套路(linux) https://blog.csdn.net/xieyihua1994/article/details/105248362/ ...

  3. 内存泄漏分析valgrind

    概述 valgrind 官网 https://www.valgrind.org/ valgrind 是 Linux 业界主流且非常强大的内存泄漏检查工具.在其官网介绍中,内存检查(memcheck)只 ...

  4. linux下使用命令行辅助定位内存泄漏问题

    文章目录 前言 一.free命令: 命令 内容解释 其它 二.top命令 命令 其它 三. cat /proc/$pid/status 命令 内容解释 总结 前言 最近自己正在做的一款产品,因内存泄漏 ...

  5. LeakCanary是如何定位内存泄漏的,看完就懂了

    文章目录 一.LeakCanary的简单使用 二.LeakCanary原理简单分析: 2-1.LeakCanary原理简述 2-2.ActivityLifecycleCallbacks使用 2-2-1 ...

  6. 【内存】内存检测工具sanitizer[内存泄漏、内存越界] VS valgrind

    简介 Sanitizers是谷歌发起的开源工具集,包括了AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer,Saniti ...

  7. 如何使用Eclipse内存分析工具定位内存泄露

    本文以我司生产环境Java应用内存泄露为案例进行分析,讲解如何使用Eclipse的MAT分析定位问题 一. 背景 11月10号晚上8点收到报警邮件,一看是OOM 打开公司监控系统查看应用各项指标发现J ...

  8. 利用MAT进行内存泄漏分析

    ##前言 对于程序员来说码代码容易,保证代码的稳定性很难.有时候写完一个功能可能只需要一天时间,但是这个功能隐藏的bug导致的线上问题排查可能需要一周或者更长时间.因此,拥有良好的代码结构和编码规范是 ...

  9. [转] 利用jemalloc分析内存泄漏

    from: https://blog.intzero.net/tools/jemalloc.html Jemalloc 不仅实现了一种通用的malloc, 还能利用它来做内存分析和监控/调优等. 这里 ...

最新文章

  1. HDU3068 最长回文
  2. 【Demo 0116】堆的使用
  3. 战棋类中实现的移动范围
  4. 【详解】以下关于TCP/IP协议栈中协议和层次的对应关系正确的是()
  5. AuthFailed at /social-auth/complete/facebook/
  6. ubuntu下面挂载mtp设备的目录位置
  7. (转)RabbitMQ学习之spring整合发送同步消息
  8. oracle12 group by 拼接字符串
  9. Oracle 10G2 for CentOS 5.2 安装截图详解
  10. border-radius几种写法的原理剖析
  11. 【方向盘】Spring Boot 2.5.0正式发布,环境变量可指定前缀的功能很赞
  12. ewiews面板回归模型操作_Eviews常用面板回归模型案例实战
  13. 2007年个人站长/SEOer必上网站
  14. python基础教程共60课-第28课字符串的分割
  15. python每天定时执行任务_Python设置定时任务
  16. java查找字符位置_java 查找字符串所在的位置代码
  17. 【DockerCE】Docker-CE 20.10.14正式版发布
  18. 训练MTCNN之前的环境以及依赖配置
  19. 米拓5.3 mysql支持off,Metinfo 5.3.17 前台SQL注入漏洞分析
  20. 【UE5】自定义环形进度条、方形进度条

热门文章

  1. 对比学习(一)-双塔模型-simCLR
  2. Jmeter 之 Beanshell
  3. 近年来火热软件有哪些创意
  4. 同文输入法 android,同文输入法(com.osfans.trime) - 3.1.3 - 应用 - 酷安
  5. 维视智造2023届校招火热进行中 快来加入我们
  6. Android-自定义圆形ProgressBar加载
  7. 生产计划进度需时刻跟进
  8. linux 让代码美观,为什么 Python 代码要写得美观而明确 | Linux 中国
  9. 红队内网渗透神器--CobaltStrike安装教程
  10. 大数据培训怎么样,怎么选择合适大数据培训机构?