• 内存泄露检测工具Valgrind


    内存泄露简介

    什么是内存泄漏

      内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因,程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
      内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。

    内存泄露产生的方式

    以产生的方式来分类,内存泄漏可以分为四类:

    • 常发性会内存泄漏:发生内存泄漏的代码会被多次执行到,每次被执行时都导致一块内存泄漏。
    • 偶发性内存泄漏:发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
    • 一次性内存泄漏:发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。
    • 隐式内存泄漏:程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

    Valgrind

    简介

      Valgrind具是一个用于调试和分析Linux程序的GPL系统。使用Valgrind的工套件,您可以自动检测许多内存管理和线程错误,使程序更稳定。还可以执行详细的分析以帮助加速程序的执行。
      Valgrind是Julian Seward的作品。Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,它包含一个内核,一个软件合成的CPU,和一系列的小工具。如下图所示:

    安装 

      CentOS安装:

    # sudo yum install valgrind -y

      Ubuntu 安装:

    # sudo apt install valgrind -y

    示例程序

      给出一个简单示例malloc.c 。

    #include<stdio.h>
    #include<stdlib.h>
    void fun()
    {
             int *x = malloc(10 * sizeof(int));
             x[10] = 0;
    }
    int main()
    {
             int i = 99;
             fun();
             printf("i = %d
    ",i);
             return 0;
    }

      以上代码存在两个问题:

    • 没有free掉申请的资源;
    • fun函数里面越界了,x[10]是非法的。

    Memcheck

      是最常用的小工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc和free的调用都会被捕获,它能检测下列问题:

    • 对未初始化内存的使用;
    • 读/写释放后的内存块;
    • 读/写超出malloc分配的内存块;
    • 读/写不适当的栈中的内存块;
    • 内存泄漏,指向一块内存的指针永远丢失;
    • 不正确的malloc/free或new/delete匹配;
    • memcpy相关函数中的dst和src指针重叠;

      我们使用Memcheck小工具来检测存在的问题。
      编译:gcc -Wall -o malloc malloc.c
      检测:valgrind ./malloc
        --show-reachable=<yes|no> [default: no]
        --leak-check=full 查看更为详细信息

      其中,34496是程序运行时的进程号。
      Invalid write of size 4:表示非法写入(越界),下面是告诉我们错误发生的位置,在main中调用的fun函数。
      HEAP SUMMARY:说明了堆的情况,可以看到申请了40个字节,后面说有1个申请,0个被free。
      LEAK SUMMARY:也是说的堆的泄漏情况,明显丢失的有40个字节。
      如果main中的i未初始化,这里还会有一些其他的错误。

    # gcc -Wall -o malloc malloc.c                                                                     
    malloc.c: In function ‘main’:
    malloc.c:22:16: warning: ‘i’ is used uninitialized in this function [-Wuninitialized]   # 未初始化变量
              printf("i = %d
    ",i);                                                                                            
                    ^
    [root@localhost memcheck]# valgrind ./malloc                                                                                
    ==34555== Memcheck,a memory error detector                                                                                 
    ==34555== Copyright (C) 2002-2017,and GNU GPL'd,by Julian Seward et al.                                                   
    ==34555== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info                                                
    ==34555== Command: ./malloc                                                                                                 
    ==34555==                                                                                                                   
    ==34555== Invalid write of size 4                                                                                           
    ==34555==    at 0x40057B: fun (in /root/memcheck/malloc)                                                                    
    ==34555==    by 0x400594: main (in /root/memcheck/malloc)                                                                   
    ==34555==  Address 0x5203068 is 0 bytes after a block of size 40 alloc'd                                                    
    ==34555==    at 0x4C29BC3: malloc (vg_replace_malloc.c:299)                                                                 
    ==34555==    by 0x40056E: fun (in /root/memcheck/malloc)
    ==34555==    by 0x400594: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Conditional jump or move depends on uninitialised value(s)
    ==34555==    at 0x4E80B9E: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Use of uninitialised value of size 8
    ==34555==    at 0x4E7E26B: _itoa_word (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E824F0: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Conditional jump or move depends on uninitialised value(s)
    ==34555==    at 0x4E7E275: _itoa_word (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E824F0: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Conditional jump or move depends on uninitialised value(s)
    ==34555==    at 0x4E8253F: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Conditional jump or move depends on uninitialised value(s)
    ==34555==    at 0x4E80C6B: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    ==34555== Conditional jump or move depends on uninitialised value(s)
    ==34555==    at 0x4E80CEE: vfprintf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4E893E8: printf (in /usr/lib64/libc-2.17.so)
    ==34555==    by 0x4005A8: main (in /root/memcheck/malloc)
    ==34555== 
    i = 0
    ==34555== 
    ==34555== HEAP SUMMARY:
    ==34555==     in use at exit: 40 bytes in 1 blocks
    ==34555==   total heap usage: 1 allocs,0 frees,40 bytes allocated
    ==34555== 
    ==34555== LEAK SUMMARY:
    ==34555==    definitely lost: 40 bytes in 1 blocks
    ==34555==    indirectly lost: 0 bytes in 0 blocks
    ==34555==      possibly lost: 0 bytes in 0 blocks
    ==34555==    still reachable: 0 bytes in 0 blocks
    ==34555==         suppressed: 0 bytes in 0 blocks
    ==34555== Rerun with --leak-check=full to see details of leaked memory
    ==34555== 
    ==34555== For counts of detected and suppressed errors,rerun with: -v
    ==34555== Use --track-origins=yes to see where uninitialised values come from
    ==34555== ERROR SUMMARY: 7 errors from 7 contexts (suppressed: 0 from 0)
    View Code

    Callgrind

      和gprof 类似的分析工具,但它对程序的运行观察更细致入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时添加附加特殊选项,但加上调试选项是推荐的。
      Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择的进行cache模拟。在运行结束时,它会把分析数据写入一个文件,callgrind_annotate可以把这个文件的内容转化成可读的形式。

      Callgrind可以帮助我们对程序的运行进行观察。

    # valgrind --tool=callgrind ./malloc

      

      可以看到生成了一个文件callgrind.out.34755,同样34755为程序运行时的进程号。当callgrind运行你的程序时,还可以使用callgrind_control来观察程序的执行,而且不会干扰它的运行。
      显示程序的详细信息:

    # callgrind_annotate callgrind.out.34755

      

    Cachegrind

      Cache分析器,它模拟CPU中的一级缓存I1,DI和二级缓存,能够精确的指出程序中cache的丢失和命中。如果需要,它还能为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块整个程序产生的指令数,这对优化程序有很大的帮助。  

    # valgrind --tool=cachegrind ./mallo

    Helgrind

      用来检测多线程程序中出现的竞争问题。Helgrind寻找内存中内对个线程访问,而又没有一贯加锁的区域。这些区域往往是线程之间失去同步的情况,而且会导致难以发掘的错误。
      Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过Helgrinf仍然处于实验阶段。

    Massif

      堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在代用虚拟内存的现代系统中,它还能加速我们程序的运行,减少程序停留在交换区中的几率。

      此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具。

    参考

      Linux下几款C++程序中的内存泄露检查工具
        https://blog.csdn.net/gatieme/article/details/51959654
      Linux下检测内存泄露的工具 valgrind
        https://cloud.tencent.com/developer/article/1075945
      Valgrind调试
        http://www.voidcn.com/article/p-xyrqgaum-yq.html

  • 相关阅读:
    SVN常用命令
    SVN部署(Centos7,Ubuntu)
    20199325 2019-2020-2 《网络攻防实践》综合实践
    20199325 2019-2020-2 《网络攻防实践》第12周作业
    20199325 2019-2020-2 《网络攻防实践》第10周作业
    20199325 2019-2020-2 《网络攻防实践》第九周作业
    20199325 2019-2020-2 《网络攻防实践》第八周作业
    20199325 2019-2020-2 《网络攻防实践》第7周作业
    20199325 2019-2020-2 《网络攻防实践》第6周作业
    20199325 2019-2020-2 《网络攻防实践》第5次作业
  • 原文地址:https://www.cnblogs.com/Hi-blog/p/Memory-Check-Tools.html
Copyright © 2020-2023  润新知