Linux C内存泄露检测工具

239 篇文章 2 订阅
147 篇文章 1 订阅

内存泄漏检查方法(for Linux)
如果你更想读原始文档, 请参考glibc info的"Allocation Debugging"一章 (执行info libc);
glibc提供了一个检查内存泄漏的方法, 前提是你的程序使用glibc的标准函数分配内存(如malloc, alloc...):
1. 在需要内存泄漏检查的代码的开始调用void mtrace(void) (在mcheck.h中有声明). mtrace为malloc等函数安装hook, 用于记录内存分配信息.在需要内存泄漏检查的代码的结束调用void muntrace(void).
注意: 一般情况下不要调用muntrace, 而让程序自然结束. 因为可能有些释放内存的的代码要到muntrace之后才运行.
2. 用debug模式编译被检查代码(-g或-ggdb)
3. 设置环境变量MALLOC_TRACE为一文件名, 这一文件将存有内存分配信息.
4. 运行被检查程序, 直至结束或muntrace被调用.
5. 用mtrace命令解析内存分配Log文件($MALLOC_TRACE)
(mtrace foo $MALLOC_TRACE, where foo is the executible name)
如果有内存泄漏, mtrace会输出分配泄漏内存的代码位置,以及分配数量.

其他东西
1. 可以将mtrace, muntrace放入信号处理函数(USR1, USR2), 以动态地进行内存泄漏检查控制.
2. mtrace是个perl代码, 如果你对符号地址与代码文本的转换感兴趣, 可以读一下.
3. again, 尽量不要用muntrace()

For C++ Leak:
检查内存泄漏的方法除glibc提供外;
还可以试试一些专用的程序;如:
ccmalloc
http://www.inf.ethz.ch/personal/biere/projects/ccmalloc/ccmalloc-english.html
mpatrol
http://www.cbmamiga.demon.co.uk/mpatrol/
这俩个工具的功能相当不错,能对程序进行相当全面的检查

 

 

 

 

 

 

 

 

01 #include <stdlib.h>

02 #include <mcheck.h>
03 
04 void func(void)
05 {
06     int* ptr = malloc(10 * sizeof(int));
07     //free(ptr);          
08 }
09 
10 int main(void)
11 {
12     setenv("MALLOC_TRACE", "output_file_name", 1);
13     mtrace();
14     func();
15     return 0;
16 }

# gcc test.c -o test -g
# ./test
# more  output_file_name 
= Start
@ ./test:[0x80483e6] + 0x804a470 0x28
@ /lib/tls/i686/cmov/libc.so.6:(clearenv+0x7c)[0xb7e449ac] - 0x804a008
@ /lib/tls/i686/cmov/libc.so.6:(tdestroy+0x47)[0xb7eec6d7] - 0x804a0c0
@ /lib/tls/i686/cmov/libc.so.6:(tdestroy+0x4f)[0xb7eec6df] - 0x804a0e8

# mtrace output_file_name
- 0x0804a008 Free 3 was never alloc'd 0xb7e449ac
- 0x0804a0c0 Free 4 was never alloc'd 0xb7eec6d7
- 0x0804a0e8 Free 5 was never alloc'd 0xb7eec6df

Memory not freed:
-----------------
0  Address     Size     Caller
0x0804a470     0x28  at 0x80483e6







01 #include <stdlib.h>
02 #include <mcheck.h>
03 
04 void func(void)
05 {
06     int* ptr = malloc(10 * sizeof(int));
07     free(ptr);          
08 }
09 
10 int main(void)
11 {
12     setenv("MALLOC_TRACE", "output_file_name", 1);
13     mtrace();
14     func();
15     return 0;
16 }


# more  output_file_name 
= Start
@ ./test:[0x8048426] + 0x804a470 0x28
@ ./test:[0x8048434] - 0x804a470
@ /lib/tls/i686/cmov/libc.so.6:(clearenv+0x7c)[0xb7e149ac] - 0x804a008
@ /lib/tls/i686/cmov/libc.so.6:(tdestroy+0x47)[0xb7ebc6d7] - 0x804a0c0
@ /lib/tls/i686/cmov/libc.so.6:(tdestroy+0x4f)[0xb7ebc6df] - 0x804a0e8 

0x8048426 分配内存空间的指令地址
0x804a470 malloc分配的内存空间己地址
+         表示分配内存
-         表示释放内存


# mtrace output_file_name 
- 0x0804a008 Free 4 was never alloc'd 0xb7e149ac
- 0x0804a0c0 Free 5 was never alloc'd 0xb7ebc6d7
- 0x0804a0e8 Free 6 was never alloc'd 0xb7ebc6df
No memory leaks.




 

 

--------------------------------------------
#include <stdlib.h>
#include <mcheck.h>

void func(void)
{
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr);         
}

int main(void)
{
    setenv("MALLOC_TRACE", "output_file_name", 1);
    mtrace();
    func();
    while (1)
    {
        muntrace();
    }
    return 0;
}


$ ./a.out &
$ more output_file_name 
= Start
@ ./a.out:[0x8048426] + 0x804a468 0x28

= End

 

 

 

 

 

--------------------------------------------
#include <stdlib.h>
#include <mcheck.h>

void func(void)
{
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr);         
}

int main(void)
{
    setenv("MALLOC_TRACE", "output_file_name", 1);
    mtrace();
    func();
    while (1)
    {
        sleep (1);
        func ();
        muntrace();
    }
    return 0;
}

= Start
@ ./a.out:[0x8048456] + 0x804a468 0x28
@ ./a.out:[0x8048456] + 0x804a498 0x28
= End
虽然while中反复调用func()但是此处只检测到两次(while外一次,while内一次)

 

 

 

 

 

---------------------------------------------
#include <stdlib.h>
#include <mcheck.h>

void func(void)
{
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr);         
}

int main(void)
{
    setenv("MALLOC_TRACE", "output_file_name", 1);
    mtrace();
    func();
    while (1)
    {
        sleep (1);
        func ();
        //muntrace();
    }
    return 0;
}


这种使用方式,进程中守护任务,所以output_file_name不会马上有内存,需要等一会儿






函数各自生成自己的内存泄露检测报告,这样比较麻烦
---------------------------------------------
#include <stdlib.h>
#include <mcheck.h>
#include <stdio.h>

void func(void)
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr); 
    muntrace();        
}

void test()
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr); 
    muntrace(); 
}

void task()
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    int* ptr = malloc(10 * sizeof(int));
    //free(ptr); 
    while (1)
    {
        usleep (10);
        /* 如果不在这里调用muntrace,将不能生成检测报告 */
        muntrace();  
    }
}

int main(void)
{

    //mtrace();
    func();
    test();
    task();
    
    return 0;
}




普通函数统一报告,有无限循环的函数单独生成报告
---------------------------------------------
#include <stdlib.h>
#include <mcheck.h>
#include <stdio.h>

void func(void)
{
    int* ptr = malloc(10 * sizeof(int));
   
}

void test()
{
    int* ptr = malloc(10 * sizeof(int));
}

void task()
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    int* ptr = malloc(10 * sizeof(int));
    while (1)
    {
        usleep (10);
        /* 如果不在这里调用muntrace,将不能生成检测报告 */
        muntrace();  
    }
}

int main(void)
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    func();
    test();
    task();
    
    return 0;
}





创建线程前检测一次,并结束检测;线程自行检测;线程创建后不能再进行检测,
如果需要检测,则不能调用muntrace(),但是检测还是会失效
---------------------------------------------
#include <stdlib.h>
#include <mcheck.h>
#include <stdio.h>
#include <pthread.h>

void func(void)
{
    int* ptr = malloc(10 * sizeof(int));
   
}

void test()
{
    int* ptr = malloc(100 * sizeof(int));
}

void task()
{
    char buff[128];
    snprintf (buff, sizeof (buff), "%s", __FUNCTION__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    int* ptr = malloc(1000 * sizeof(int));
    while (1)
    {
        usleep (10);
        /* 如果不在这里调用muntrace,将不能生成检测报告 */
        muntrace();  
    }
}

int main(void)
{
    pthread_t id;
    int i, ret;

    char buff[128];
    snprintf (buff, sizeof (buff), "%s%d", __FUNCTION__, __LINE__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    func();
    test();
    muntrace();

    ret = pthread_create(&id, NULL, (void *)task, NULL);
    if (ret != 0)
    {
        printf("Create pthread error!\n");
        exit(1);
    }

#if 0 /* 创建线程(线程自行负责检测)后,就不能再检测了,否则会有段错误 */
    snprintf (buff, sizeof (buff), "%s%d", __FUNCTION__, __LINE__);
    setenv("MALLOC_TRACE", buff, 1);
    mtrace();
    test();    
    //muntrace();
#endif

    sleep(1);
    //while (1) {}
    return 0;
}


Valgrind

内存检测工具1. ccmalloc-Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库。 
2. Dmalloc-Debug Malloc Library. 
3. Electric Fence-Linux分发版中由Bruce Perens编写的malloc()调试库。 
4. Leaky-Linux下检测内存泄漏的程序。 
5. LeakTracer-Linux、Solaris和HP-UX下跟踪和分析C++程序中的内存泄漏。 
6. MEMWATCH-由Johan Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gcc的precessor来进行。 
7. Valgrind-Debugging and profiling Linux programs, aiming at programs written in C and C++. 
8. KCachegrind-A visualization tool for the profiling data generated by Cachegrind and Calltree. 

13. DTrace-即动态跟踪Dynamic Tracing,是一款开源软件,能在Unix类似平台运行,用户能够动态检测操作系统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。
14. IBM Rational PurifyPlus-帮助开发人员查明C/C++、托管.NET、Java和VB6代码中的性能和可靠性错误。PurifyPlus 将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。 
15. Parasoft Insure++-针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和I/O 等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为 Microsoft Visual C++的一个插件运行。 
16. Compuware DevPartner for Visual C++ BoundsChecker Suite-为C++开发者设计的运行错误检测和调试工具软件。作为Microsoft Visual Studio和C++ 6.0的一个插件运行。 
17. Electric Software GlowCode-包括内存泄漏检查,code profiler,函数调用跟踪等功能。给C++和.Net开发者提供完整的错误诊断,和运行时性能分析工具包。
18. Compuware DevPartner Java Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。 
19. Quest JProbe-分析Java的内存泄漏。 
20. ej-technologies JProfiler-一个全功能的Java剖析工具,专用于分析J2SE和J2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。 JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问 题。4.3.2注册码:A-G666#76114F-1olm9mv1i5uuly#0126 
21. BEA JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。 
22. SciTech Software AB .NET Memory Profiler-找到内存泄漏并优化内存使用针对C#,VB.Net,或其它.Net程序。
23. YourKit .NET & Java Profiler-业界领先的Java和.NET程序性能分析工具。

26 BChecker6

附录:内存泄漏的发生方式 
1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 
2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。 
3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。 
4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但 是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。 

什么是系统资源? 
当应用程序在Windows中运行时,Windows必须实时"跟踪"该应用程序的运行,并保留与之相关的许多信息,如按钮、光标、菜单的位置和位图、窗 口的状况等,这些信息由Windows保留在一种叫堆的内存块中,堆的英文为Heap。简单地说,堆是采用特殊机制管理的内存块。由Windows的一个 系统内核User.exe管理的堆叫做User资源堆(User Resource Heap),由另一个系统内核Gdi.exe管理的堆叫做GDI资源堆(Graphical Device Interface Resource Heap,简称GDI Resource Heap),User资源堆和GDI资源堆合称为系统资源堆(System Resource Heap),习惯上就把它们叫做系统资源(System Resource)。 
  微软将Windows的系统资源(堆)分为五个堆,其中User资源堆为三个,而GDI资源堆为两个。 
  三个User资源堆分别是:16位的用户堆(User Heap,64KB);32位的窗口堆(Windows Heap,2MB);32位的用户菜单堆(User Menu Heap,2MB)。 
  两个GDI资源堆分别是:16位的GDI堆(GDI Heap,64KB);32位的GDI堆(GDI,2MB)。 
  从这里的系统资源分类和大小我们应该明白,不管CPU是P4还是486,内存是8M还是1G,所有Windows的用户都拥有同样大小的系统资源(堆),用户不能自已增加或减少系统资源的大小,这是由操作系统决定的,与硬件档次没有任何关系。 
  Windows的User资源堆和GDI资源堆的可用(Free)空间称为可用 User资源和可用GDI资源,Windows中以百分数表示它们,用户可以选择 "开始/附件/系统工具/系统信息",来实时查看它们的大小。
各位朋友 给些建议哦(=_=)


c++内存检查函数

在c++中有内存溢出的处理函数,包含在<new>,void nomoreMemory( ) {cerr<<"内存不足\n";abort( );} int main(){ set_new_handler(nomoreMemory);
主要的是set_new_handler函数,传进去的是一个无返回值的无参函数

所有使用动态内存分配(dynamic memory allocation)的程序都有机会遇上内存泄露(memory leakage)问题,在Linux里有三种常用工具来检测内存泄露的情況,包括:

   1. mtrace
   2. dmalloc
   3. memwatch

1. mtrace

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」吧。
    若我们把该段内存释放:
    #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;
        }

        free(hello);
        return 0;
    }

结果如下:
    - 0x080496b0 Free 4 was never alloc'd 0x42029acc
    - 0x08049730 Free 5 was never alloc'd 0x420dc9e9
    - 0x08049748 Free 6 was never alloc'd 0x420dc9f1
    - 0x08049668 Free 7 was never alloc'd 0x42113a22
    - 0x08049680 Free 8 was never alloc'd 0x42113a52
    - 0x08049698 Free 9 was never alloc'd 0x42113a96
    No memory leaks.

mtrace的原理是记录每一对malloc-free的执行,若每一个malloc都有相应的free,则代表没有内存泄露,对于任何非malloc/free情況下所发生的内存泄露问题,mtrace并不能找出来。


Memwatch简介

在三种检测工具当中,设置最简单的算是memwatch,和dmalloc一样,它能检测未释放的内存、同一段内存被释放多次、位址存取错误及不当使用未分配之内存区域。请往http://www.linkdata.se/sourcecode.html下载最新版本的Memwatch。
安装及使用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,则会放弃写入文件。

我们引用第一篇(mtrace)中所使用过的有问题的代码:
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <memwatch.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;
    }

然后在shell中输入以下编译指令:
    gcc -DMEMWATCH -DMW_STDIO test.c memwatch.c -o test

memwatch.log的內容如下:
    ============= MEMWATCH 2.71 Copyright (C) 1992-1999 Johan Lindh =============

    Started at Sat Jun 26 22:48:47 2004

    Modes: __STDC__ 32-bit mwDWORD==(unsigned long)
    mwROUNDALLOC==4 sizeof(mwData)==32 mwDataSize==32


    Stopped at Sat Jun 26 22:48:47 2004

        unfreed: <1> test.c(9), 1 bytes at 0x805108c    {FE .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .}

    Memory usage statistics (global):
     N)umber of allocations made: 1
     L)argest memory usage      : 1
     T)otal of all alloc() calls: 1
     U)nfreed bytes totals      : 1

文件指出,在test.c被执行到第9行时所分配的内存仍未被释放,该段内存的大小为1 byte。
Memwatch使用注意

Memwatch 的优点是无需特別配置,不需安装便能使用,但缺点是它会拖慢程序的运行速度,尤其是释放内存时它会作大量检查。但它比mtrace和dmalloc多了一项功能,就是能模拟系统内存不足的情況,使用者只需用mwLimit(long num_of_byte)函数来限制程式的heap memory大小(以byte单位)。

最详细的使用说明(包括优点缺点,运行原理等)已在README中列出,本人强烈建议各位读者参考该文件。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值