zoukankan      html  css  js  c++  java
  • valgrind 性能测试工具学习使用

    一、valgrind简介

    Valgrind工具套件提供了许多调试和分析工具,可帮助您使程序更快,更正确。这些工具中最受欢迎的是Memcheck。它可以检测许多与C和C ++程序中常见的内存相关的错误,这些错误可能导致崩溃和不可预测的行为。

    标准配置提供了许多有用的工具:

    1. Memcheck是一个内存错误检测器。可以帮助你使程序更加正确。——这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。
    2. Cachegrind是一个缓存和分支预测分析器。可以帮助程序运行的更快。——它主要用来检查程序中缓存使用出现的问题。
    3. Callgrind是一个生成缓存分析器的调用图。和Cachegrind有一些重叠,也收集了Cachegrind没有的一些信息。——它主要用来检查程序中函数调用过程中出现的问题。
    4. Helgrind是一个线程错误检测器。可以帮助多线程程序更准确。——它主要用来检查多线程程序中出现的竞争问题。
    5. DRD也是线程错误检测器。他与Helgrind类似,但是用不同的分析技术,因此可能会发现不同的问题。
    6. Massif时一个堆分析器。可以帮助使程序使用更少的内存。——它主要用来检查程序中堆栈使用中出现的问题。
    7. DHAT时一种不同类型的对分析器。可以帮助了解块寿命,块利用率和效率低下的问题。
    8. SGcheck时一种实验工具,可以检测堆栈和全局数组的溢出。它的功能与Memcheck的功能互补:SGcheck发现Memcheck无法解决的问题,反之亦然。
    9. 小工具: Lackey是一个示例工具,用于说明一些仪器基础知识
    10. 小工具:Nulgrind是最小的Valgrind工具,不进行分析或检测,仅用于测试目的。是最简单的Valgrind工具。它不执行任何程序或程序分析,只需正常运行即可。它主要用于Valgrind的开发人员进行调试和回归测试。尽管如此,您可以使用Nulgrind运行程序。它们的运行速度比正常情况慢5倍左右,没有任何有用的效果。Note that you need to use the option --tool=none to run Nulgrind (ie. not --tool=nulgrind).
    11. extension ------> 可以利用core提供的功能,自己编写特定的内存调试工具(不在我学习的范围中)

     二、Ubuntu16.04下安装valgrind

    Valgrind 安装,去官网下载: http://valgrind.org/downloads/current.html#current

    我当前下载的版本为valgrind-3.15.0.tar.bz2

    # tar -jxf valgrind-3.15.0.tar.bz2   ——解压
    
    # cd /home/ranxf/valgrind-3.15.0
    
    # vim README ——查看文档来确认安装过程
    
     0. Clone the code from GIT:
         git clone git://sourceware.org/git/valgrind.git
         There are further instructions at
         http://www.valgrind.org/downloads/repository.html.
    
     1. cd into the source directory.
    
      2. Run ./autogen.sh to setup the environment (you need the standard
         autoconf tools to do so).
    
      3. Continue with the following instructions...
    
      To install from a tar.bz2 distribution:
    
      4. Run ./configure, with some options if you wish.  The only interesting
         one is the usual --prefix=/where/you/want/it/installed.
    
      5. Run "make".
    
      6. Run "make install", possibly as root if the destination permissions
         require that.
    
      7. See if it works.  Try "valgrind ls -l".  Either this works, or it
         bombs out with some complaint.  In that case, please let us know
         (see http://valgrind.org/support/bug_reports.html).
    
    # ./configure --prefix=/workdisk/valgrind/  # 配置安装路径
    # make  # 从makefile中读取指令,然后编译
    # make instal  # 从makefile中读取指令,安装到指定位置

    # vim /etc/profile
    export PATH="$PATH:/workdisk/valgrind/bin"(环境变量配置)

    三、验证valgrind是否安装成功

    cd /workdisk/valgrind/bin

    # ./valgrind ls -l
    ==31242== Memcheck, a memory error detector
    ==31242== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==31242== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==31242== Command: ls -l
    ==31242== 
    总用量 836
    -rwxr-xr-x 1 root root  44737 8月  27 10:41 callgrind_annotate
    -rwxr-xr-x 1 root root  12673 8月  27 10:41 callgrind_control
    -rwxr-xr-x 1 root root  33535 8月  27 10:41 cg_annotate
    -rwxr-xr-x 1 root root  10418 8月  27 10:41 cg_diff
    -rwxr-xr-x 1 root root 315872 8月  27 10:41 cg_merge
    -rwxr-xr-x 1 root root  24398 8月  27 10:41 ms_print
    -rwxr-xr-x 1 root root  76816 8月  27 10:41 valgrind
    -rwxr-xr-x 1 root root 104136 8月  27 10:41 valgrind-di-server
    -rwxr-xr-x 1 root root  30176 8月  27 10:41 valgrind-listener
    -rwxr-xr-x 1 root root 180680 8月  27 10:41 vgdb
    ==31242== 
    ==31242== HEAP SUMMARY:
    ==31242==     in use at exit: 19,848 bytes in 17 blocks
    ==31242==   total heap usage: 228 allocs, 211 frees, 102,284 bytes allocated
    ==31242== 
    ==31242== LEAK SUMMARY:
    ==31242==    definitely lost: 0 bytes in 0 blocks
    ==31242==    indirectly lost: 0 bytes in 0 blocks
    ==31242==      possibly lost: 0 bytes in 0 blocks
    ==31242==    still reachable: 19,848 bytes in 17 blocks
    ==31242==         suppressed: 0 bytes in 0 blocks
    ==31242== Rerun with --leak-check=full to see details of leaked memory
    ==31242== 
    ==31242== For lists of detected and suppressed errors, rerun with: -s
    ==31242== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    ./valgrind --leak-check=full ls -l
    ==31322== Memcheck, a memory error detector
    ==31322== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==31322== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==31322== Command: ls -l
    ==31322== 
    总用量 836
    -rwxr-xr-x 1 root root  44737 8月  27 10:41 callgrind_annotate
    -rwxr-xr-x 1 root root  12673 8月  27 10:41 callgrind_control
    -rwxr-xr-x 1 root root  33535 8月  27 10:41 cg_annotate
    -rwxr-xr-x 1 root root  10418 8月  27 10:41 cg_diff
    -rwxr-xr-x 1 root root 315872 8月  27 10:41 cg_merge
    -rwxr-xr-x 1 root root  24398 8月  27 10:41 ms_print
    -rwxr-xr-x 1 root root  76816 8月  27 10:41 valgrind
    -rwxr-xr-x 1 root root 104136 8月  27 10:41 valgrind-di-server
    -rwxr-xr-x 1 root root  30176 8月  27 10:41 valgrind-listener
    -rwxr-xr-x 1 root root 180680 8月  27 10:41 vgdb
    ==31322== 
    ==31322== HEAP SUMMARY:
    ==31322==     in use at exit: 19,848 bytes in 17 blocks
    ==31322==   total heap usage: 228 allocs, 211 frees, 102,284 bytes allocated
    ==31322== 
    ==31322== LEAK SUMMARY:
    ==31322==    definitely lost: 0 bytes in 0 blocks
    ==31322==    indirectly lost: 0 bytes in 0 blocks
    ==31322==      possibly lost: 0 bytes in 0 blocks
    ==31322==    still reachable: 19,848 bytes in 17 blocks
    ==31322==         suppressed: 0 bytes in 0 blocks
    ==31322== Reachable blocks (those to which a pointer was found) are not shown.
    ==31322== To see them, rerun with: --leak-check=full --show-leak-kinds=all
    ==31322== 
    ==31322== For lists of detected and suppressed errors, rerun with: -s
    ==31322== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

    四、Valgrind 命令参数介绍

    用法: valgrind [options] prog-and-args
    [options]: 常用选项,适用于所有Valgrind工具

    最常用的命令格式:

    valgrind --tool=memcheck --leak-check=full ./test

        -h –help 显示帮助信息。(除了以下参数,可用帮助-h查看更多信息)
        --version 显示valgrind内核的版本,每个工具都有各自的版本。
        -q –quiet 安静地运行,只打印错误信息。
        -v –verbose 更详细的信息, 增加错误数统计。
        --trace-children=no|yes 跟踪子线程? [no]
        --track-fds=no|yes 跟踪打开的文件描述?[no]
        --time-stamp=no|yes 增加时间戳到LOG信息? [no]
        --log-fd=<number> 输出LOG到描述符文件 [2=stderr]
        --log-file=<file> 将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID
        --log-file-exactly=<file> 输出LOG信息到 file
        --log-file-qualifier=<VAR> 取得环境变量的值来做为输出信息的文件名。 [none]
        --log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port

    LOG信息输出

    还是在-h中查看

       --xml=yes 将信息以xml格式输出,只有memcheck可用
        --num-callers=<number> show <number> callers in stack traces [12]
        --error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]
        --error-exitcode=<number> 如果发现错误则返回错误代码 [0=disable]
        --db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]
        --db-command=<command> 启动调试器的命令行选项[gdb -nw %f %p]

    适用于Memcheck工具的相关选项

       --leak-check=no|summary|full 要求对leak给出详细信息? [summary]
        --leak-resolution=low|med|high how much bt merging in leak check [low]
        --show-reachable=no|yes show reachable blocks in leak check? [no]

    五、测试案例介绍

    (一)测试demo

    /workdisk# vim /workdisk/testcode/testValgrind.cc  # 在该路径下生成喊以下内容的testValgrind.cc文件(内存未释放)

    #include <iostream> using namespace std; int main() { int *a = new int(2); //delete a; return 0; }

    (二)代码编译

    root@ranxf-TEST:/workdisk/testcode# ls
    testValgrind.cc
    root@ranxf-TEST:/workdisk/testcode# g++ -g -o testValgrind testValgrind.cc 
    root@ranxf-TEST:/workdisk/testcode# ls
    testValgrind  testValgrind.cc

    -o:指定生成可执行文件的名称。使用方法为:g++ -o afile file.cpp file.h ... (可执行文件不可与待编译或链接文件同名,否则会生成相应可执行文件且覆盖原编译或链接文件),如果不使用-o选项,则会生成默认可执行文件a.out。
    -c:只编译不链接,只生成目标文件。
    -g:添加gdb调试选项。

    (三)代码单元测试

    输入命令:orkdisk/testcode# /workdisk/valgrind/bin/valgrind --tool=memcheck --leak-check=full ./testValgrind
    如果valgrind添加了环境变量,输入命令可以在任何路径下简化为:#valgrind --tool=memcheck --leak-check=full ./testValgrin
    root@ranxf-TEST:/workdisk/testcode# /workdisk/valgrind/bin/valgrind --tool=memcheck --leak-check=full ./testValgrind
    ==12862== Memcheck, a memory error detector    # 内存错误检测器
    ==12862== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==12862== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==12862== Command: ./testValgrind
    ==12862== 
    ==12862== 
    ==12862== HEAP SUMMARY:  # 堆摘要
    ==12862==     in use at exit: 72,708 bytes in 2 blocks # 退出时使用:2个数据块中有72708个字节
    ==12862==   total heap usage: 2 allocs, 0 frees, 72,708 bytes allocated
    ==12862== 
    ==12862== 4 bytes in 1 blocks are definitely lost in loss record 1 of 2  # 1个块中的4个字节在丢失记录1(共2个)中肯定丢失
    ==12862==    at 0x4C2E4B6: operator new(unsigned long) (vg_replace_malloc.c:344)
    ==12862==    by 0x400717: main (testValgrind.cc:7)
    ==12862== 
    ==12862== LEAK SUMMARY:  泄漏汇总
    ==12862==    definitely lost: 4 bytes in 1 blocks  肯定泄露:1块中有4个字节
    ==12862==    indirectly lost: 0 bytes in 0 blocks  间接泄露:0块0字节
    ==12862==      possibly lost: 0 bytes in 0 blocks  可能泄露:0块0字节
    ==12862==    still reachable: 72,704 bytes in 1 blocks  仍然可以访问:1个块中有72704个字节
    ==12862==         suppressed: 0 bytes in 0 blocks   抑制:0块0字节
    ==12862== Reachable blocks (those to which a pointer was found) are not shown. 不显示可访问块(找到指针的块)。
    ==12862== To see them, rerun with: --leak-check=full --show-leak-kinds=all   若要查看它们,请使用以下项重新运行:--leak check=full--show leak kinds=all
    ==12862== 
    ==12862== For lists of detected and suppressed errors, rerun with: -s  对于检测到的和抑制的错误列表,请使用-s重新运行。
    ==12862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)  错误摘要:1个上下文中有1个错误(禁止:0到0)
    root@ranxf-TEST:/workdisk/testcode# 

    Memcheck将内存泄露分为两种,一种是肯定的内存泄露(definitely lost),一种可能的内存泄露(possibly lost);

    Definitely lost 是指已经不能够访问这块内存;

      Definitely lost又分为两种:直接的(direct)泄露和间接的(indirect)泄露

        直接泄露是没有任何指针指向该内存;

        间接泄露是指指向该内存的指针都位于内存泄露处;

    Possibly lost 是指仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首地址。

     (四)测试代码

    代码1. 使用未初始化的内存(程序名为valgrindTestCode1.cpp

    #include <iostream>
    using namespace std;
    int main()
    {
        int a[5];
        int i,s=0;
        a[0]=a[1]=a[3]=a[4]=0;
        for(i=0;i<5;i++)
          s=s+a[i];
        if(s==33)
          cout<<"sum is 33"<<endl;
        else
          cout<<"sum is not 33"<<endl;
        return 0;
    }

    数组a中的第二个元素未进行初始化,但是在for循环中依然访问链数组a的第二个元素。这就是使用未初始化的元素问题。在使用g++编译器对该程序编译并运行,结果如下:

    root@ranxf-TEST:/workdisk/testcode# g++ -g -o TectCode1 valgrindTestCode1.cpp 
    root@ranxf-TEST:/workdisk/testcode# ls
    TectCode1  testValgrind  testValgrind.cc  valgrindTestCode1.cpp
    root@ranxf-TEST:/workdisk/testcode# ./TectCode1 
    sum is not 33

    程序在编译和运行过程中并没有报出任何错误,异常和警告。

    但实际上,程序存在一个巨大的隐患,那就是对未初始化的内存进行访问。我们使用valgrind对该程序进行检测,结果如下所示:

    root@ranxf-TEST:/workdisk/testcode# valgrind ./TectCode1  或者 valgrind --tool=memcheck --leak-check=full ./TectCode1  # valgrind默认为内存错误检测器
    ==22985== Memcheck, a memory error detector
    ==22985== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==22985== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==22985== Command: ./TectCode1
    ==22985== 
    ==22985== Conditional jump or move depends on uninitialised value(s)
    ==22985==    at 0x400910: main (valgrindTestCode1.cpp:10)  (此处要求我们在进行g++编译时添加-g参数,不然便给不出具体行数的提示)
    ==22985== 
    sum is not 33
    ==22985== 
    ==22985== HEAP SUMMARY:
    ==22985==     in use at exit: 72,704 bytes in 1 blocks
    ==22985==   total heap usage: 2 allocs, 1 frees, 73,728 bytes allocated
    ==22985== 
    ==22985== LEAK SUMMARY:
    ==22985==    definitely lost: 0 bytes in 0 blocks
    ==22985==    indirectly lost: 0 bytes in 0 blocks
    ==22985==      possibly lost: 0 bytes in 0 blocks
    ==22985==    still reachable: 72,704 bytes in 1 blocks
    ==22985==         suppressed: 0 bytes in 0 blocks
    ==22985== Rerun with --leak-check=full to see details of leaked memory

    从上图结果中可以清晰的看到,提示出1个错误,提示信息为:“Conditional jump or move depends on uninitialised value(s)”,并且提示了错误位置在源文件test1.cpp中的第10行(此处要求我们在进行g++编译时添加-g参数,不然便给不出具体行数的提示)

    代码2:内存读写越界

    
    
     1 #include <stdlib.h>
     2 #include <iostream>
     3 using namespace std;
     4 int main()
     5 {
     6     int len=4;
     7     int *pt=(int *)malloc(len*sizeof(int));
     8     int *p=pt;
     9     for(int i=0;i<len;i++)
    10       p++;
    11     *p=5;
    12     cout<<"the value of p is"<<*p<<endl;
    13     return 0;
    14 }
    
    
    在程序2中,p指针首先指向了malloc出的4个字节的地址。
    之后执行了四次自增运算。也就是p指针自增运算后与初始地址相比偏移了32个字节。
    偏移后的地址空间在程序中并没有申请。所以,p指针变成链野指针。
    程序中并且又对p的地址空间进行写操作。这就造成链内存读写越界的问题。
     ———————————————— 
    在使用g++编译器对该程序编译并运行,结果如下: root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode2 valgrindTestCode2.cpp root@ranxf-TEST:/workdisk/testcode# ./TestCode2 the value of p is5
    /workdisk/testcode# valgrind ./TestCode2
    ==26714== Memcheck, a memory error detector
    ==26714== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==26714== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==26714== Command: ./TestCode2
    ==26714== 
    ==26714== Invalid write of size 4
    ==26714==    at 0x400921: main (valgrindTestCode2.cpp:11)
    ==26714==  Address 0x5ab7c90 is 0 bytes after a block of size 16 alloc'd
    ==26714==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
    ==26714==    by 0x4008F6: main (valgrindTestCode2.cpp:7)
    ==26714== 
    ==26714== Invalid read of size 4
    ==26714==    at 0x40092B: main (valgrindTestCode2.cpp:12)
    ==26714==  Address 0x5ab7c90 is 0 bytes after a block of size 16 alloc'd
    ==26714==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
    ==26714==    by 0x4008F6: main (valgrindTestCode2.cpp:7)
    ==26714== 
    the value of p is5
    ==26714== 
    ==26714== HEAP SUMMARY:
    ==26714==     in use at exit: 72,720 bytes in 2 blocks
    ==26714==   total heap usage: 3 allocs, 1 frees, 73,744 bytes allocated
    ==26714== 
    ==26714== LEAK SUMMARY:
    ==26714==    definitely lost: 16 bytes in 1 blocks
    ==26714==    indirectly lost: 0 bytes in 0 blocks
    ==26714==      possibly lost: 0 bytes in 0 blocks
    ==26714==    still reachable: 72,704 bytes in 1 blocks
    ==26714==         suppressed: 0 bytes in 0 blocks
    ==26714== Rerun with --leak-check=full to see details of leaked memory
    ==26714== 
    ==26714== For lists of detected and suppressed errors, rerun with: -s
    ==26714== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

    从上图结果中可以清晰的看到,提示出2个错误,提示信息分别为:“Invalid write of size 4”和“Invalid read of size 4”,并且提示了错误位置在源文件test2.cpp中的第11行和第12行。并且检测提示了:“total heap usage: 3 allocs, 1 frees, 73,744 bytes allocated”。有内存泄漏。

    代码3. 内存覆盖

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main()
    {
        char x[50];
        int i;
        for(i=0;i<50;i++)
          x[i]=i+1;
        strncpy(x+20,x,20);
        strncpy(x+20,x,21);
        strncpy(x,x+20,20);
        strncpy(x,x+20,21);
        x[39]='';
        strcpy(x,x+20);
        x[39]=39;
        x[40]='';
        strcpy(x,x+20);
        return 0;
    }
    
    在代码3中,strncpy(x+20,x,21),strncpy(x,x+20,21),strcpy(x,x+20)这三条语句,在进行字符串复制过程中,复制与被复制的字符串空间存在交集。
    通过复制操作,会改变初始字符串空间中的值。这就是内存覆盖的问题。在使用g++编译器对该程序编译并运行,结果如下: root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode3 valgrindTestCode3.cpp root@ranxf-TEST:/workdisk/testcode# ./TestCode3 root@ranxf-TEST:/workdisk/testcode#
    可以从上图中发现,程序在编译和运行过程中并没有报出任何错误,异常和警告。
    但实际上存在内存覆盖的问题,当我们不注意,再去使用原始字符串时,已经不是原来那个字符串了。
    我们使用valgrind对该程序进行检测,结果如下所示: root@ranxf
    -TEST:/workdisk/testcode# valgrind ./TestCode3 ==30101== Memcheck, a memory error detector ==30101== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==30101== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info ==30101== Command: ./TestCode3 ==30101== ==30101== Source and destination overlap in strncpy(0x1fff0003c9, 0x1fff0003b5, 21) ==30101== at 0x4C31706: __strncpy_sse2_unaligned (vg_replace_strmem.c:555) ==30101== by 0x400648: main (valgrindTestCode3.cpp:11) ==30101== ==30101== Source and destination overlap in strncpy(0x1fff0003b5, 0x1fff0003c9, 21) ==30101== at 0x4C31706: __strncpy_sse2_unaligned (vg_replace_strmem.c:555) ==30101== by 0x400680: main (valgrindTestCode3.cpp:13) ==30101== ==30101== Source and destination overlap in strcpy(0x1fff0003a0, 0x1fff0003b4) ==30101== at 0x4C311C6: strcpy (vg_replace_strmem.c:513) ==30101== by 0x4006BA: main (valgrindTestCode3.cpp:18) ==30101== ==30101== ==30101== HEAP SUMMARY: ==30101== in use at exit: 0 bytes in 0 blocks ==30101== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==30101== ==30101== All heap blocks were freed -- no leaks are possible ==30101== ==30101== For lists of detected and suppressed errors, rerun with: -s ==30101== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

    提示三个错误,红色标识部分,并提示源文件中的错误行号11行,13行和18行

    代码4、动态内存管理错误

     1 #include <iostream>
     2 #include <stdlib.h>
     3 int main()
     4 {
     5     int i;
     6     char *p=(char *)malloc(10);
     7     char *pt=p;
     8     for(i=0;i<10;i++)
     9     {
    10         p[i]='z';
    11     }
    12     delete p;
    13     pt[1]='x';
    14     free(pt);
    15     return 0;
    16 }
    在程序4中,使用malloc申请空间,使用delete释放空间,两者不匹配。使用malloc申请了10个字节的空间,只释放了一个字节空间。另外,对释放的空间仍然进行了读写操作。这些是典型的内存管理错误问题。在使用g++编译器对该程序编译并运行,结果如下
    
    
    root@ranxf-TEST:/workdisk/testcode# g++ -g -o TestCode4 valgrindTestCode4.cpp 
    root@ranxf-TEST:/workdisk/testcode# ./TestCode4
    *** Error in `./TestCode4': double free or corruption (fasttop): 0x00000000023dfc20 ***
    ……
    已放弃 (核心已转储)
    
    
    程序在运行中报出内存错误,但没有给出具体信息。我们使用valgrind对该程序进行检测,结果如下所示
    
    root@ranxf-TEST:/workdisk/testcode# valgrind ./TestCode4
    ==31851== Memcheck, a memory error detector
    ==31851== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==31851== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==31851== Command: ./TestCode4
    ==31851== 
    ==31851== Mismatched free() / delete / delete []
    ==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
    ==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
    ==31851==  Address 0x5ab7c80 is 0 bytes inside a block of size 10 alloc'd
    ==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
    ==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
    ==31851== 
    ==31851== Invalid write of size 1
    ==31851==    at 0x4007EB: main (valgrindTestCode4.cpp:13)
    ==31851==  Address 0x5ab7c81 is 1 bytes inside a block of size 10 free'd
    ==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
    ==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
    ==31851==  Block was alloc'd at
    ==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
    ==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
    ==31851== 
    ==31851== Invalid free() / delete / delete[] / realloc()
    ==31851==    at 0x4C2EF90: free (vg_replace_malloc.c:540)
    ==31851==    by 0x4007F9: main (valgrindTestCode4.cpp:14)
    ==31851==  Address 0x5ab7c80 is 0 bytes inside a block of size 10 free'd
    ==31851==    at 0x4C2F440: operator delete(void*) (vg_replace_malloc.c:586)
    ==31851==    by 0x4007E2: main (valgrindTestCode4.cpp:12)
    ==31851==  Block was alloc'd at
    ==31851==    at 0x4C2DE96: malloc (vg_replace_malloc.c:309)
    ==31851==    by 0x4007A7: main (valgrindTestCode4.cpp:6)
    ==31851== 
    ==31851== 
    ==31851== HEAP SUMMARY:
    ==31851==     in use at exit: 72,704 bytes in 1 blocks
    ==31851==   total heap usage: 2 allocs, 2 frees, 72,714 bytes allocated
    ==31851== 
    ==31851== LEAK SUMMARY:
    ==31851==    definitely lost: 0 bytes in 0 blocks
    ==31851==    indirectly lost: 0 bytes in 0 blocks
    ==31851==      possibly lost: 0 bytes in 0 blocks
    ==31851==    still reachable: 72,704 bytes in 1 blocks
    ==31851==         suppressed: 0 bytes in 0 blocks
    ==31851== Rerun with --leak-check=full to see details of leaked memory
    ==31851== 
    ==31851== For lists of detected and suppressed errors, rerun with: -s
    ==31851== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
    root@ranxf-TEST:/workdisk/testcode# 
    从上图结果中可以清晰的看到,提示出的3个错误。
      使用malloc申请空间,使用delete释放空间,两者不匹配问题:
        “Mismatched free() / delete / delete []”;
      使用malloc申请链10个字节的空间,只释放链一个字节空间:
        “ Invalid free() / delete / delete[] / realloc()”;
      对释放空间仍然进行读写操作:
        “Invalid write of size 1”;

    代码5:内存泄露

    以下代码中分别存在makefile  test.cpp  tree.cpp  tree.h四个文件中

     1 /* makefile文件
     2 
     3 test:test.o tree.o
     4         g++ -g -o test test.o tree.o
     5 tree.o:tree.cpp tree.h
     6         g++ -g -c tree.cpp -o tree.o
     7 test.o:test.cpp
     8         g++ -g -c test.cpp -o test.o
     9 
    10 
    /* tree.h文件
    12 #ifndef _TREE_
    13 #define _TREE_
    14 typedef struct _node{
    15     struct _node *l;
    16     struct _node *r;
    17     char v;
    18 }node;
    19 node *mk(node *l,node *r,char val);
    20 void nodefr(node *n);
    21 #endif
    22 
    23 
    / * test.cpp文件
    25 #include <iostream>
    26 #include "tree.h"
    27 int main()
    28 {
    29         node *tree1,*tree2,*tree3;
    30         tree1=mk(mk(mk(0,0,'3'),0,'2'),0,'1');
    31         tree2=mk(0,mk(0,mk(0,0,'6'),'5'),'4');
    32         tree3=mk(mk(tree1,tree2,'8'),0,'7');
    33         return 0;
    34 }
    35 
    /* tree.cpp文件
    37 
    38 #include <stdlib.h>
    39 #include "tree.h"
    40 node *mk(node *l,node *r,char val)
    41 {
    42         node *f=(node *)malloc(sizeof(*f));
    43         f->l=l;
    44         f->r=r;
    45         f->v=val;
    46         return f;
    47 }
    48 void nodefr(node *n)
    49 {
    50         if(n){
    51                 nodefr(n->l);
    52                 nodefr(n->r);
    53                 free(n);
    54         }
    55 }

    在程序5中,使用malloc申请空间后,没有使用free函数释放申请的内存地址造成内存泄漏。在使用g++编译器对该程序编译并运行,结果如下:

    1 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ls
    2 makefile  test.cpp  tree.cpp  tree.h
    3 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# make
    4 g++ -g -c test.cpp -o test.o
    5 g++ -g -c tree.cpp -o tree.o
    6 g++ -g -o test test.o tree.o
    7 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# 

    root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ls
    makefile  test  test.cpp  test.o  tree.cpp  tree.h  tree.o
    root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# ./test

    可以从上图中发现,程序在编译和运行过程中并没有报出任何错误,异常和警告。但实际上存在内存泄漏的问题。我们使用valgrind对该程序进行检测,结果如下所示:

     1 root@ranxf-TEST:/workdisk/testcode/valgrindTestCode5# valgrind ./test
     2 ==6852== Memcheck, a memory error detector
     3 ==6852== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
     4 ==6852== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
     5 ==6852== Command: ./test
     6 ==6852== 
     7 ==6852== 
     8 ==6852== HEAP SUMMARY:
     9 ==6852==     in use at exit: 72,896 bytes in 9 blocks
    10 ==6852==   total heap usage: 9 allocs, 0 frees, 72,896 bytes allocated
    11 ==6852== 
    12 ==6852== LEAK SUMMARY:
    13 ==6852==    definitely lost: 24 bytes in 1 blocks   # 提示直接内存泄露
    14 ==6852==    indirectly lost: 168 bytes in 7 blocks  # 提示间接内存泄露
    15 ==6852==      possibly lost: 0 bytes in 0 blocks
    16 ==6852==    still reachable: 72,704 bytes in 1 blocks
    17 ==6852==         suppressed: 0 bytes in 0 blocks
    18 ==6852== Rerun with --leak-check=full to see details of leaked memory
    19 ==6852== 
    20 ==6852== For lists of detected and suppressed errors, rerun with: -s
    21 ==6852== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

    由此看到:“total heap usage: 8 allocs, 0 frees, 96 bytes allocated”。

    提示间接内存泄漏:“indirectly lost: 84 bytes in 7 blocks”;

    提示直接内存泄漏:“definitely lost: 12 bytes in 1 blocks”

    间接内存泄漏指:指向该内存的指针都位于内存泄漏处;

    直接泄漏时指:没有任何指针指向该内存。

    本篇中的代码样例及相关内容都来自于学习徐晓鑫女士编著的《后台开发核心技术与应用实践》所得,特此声明。

    程序相关源码下载地址:https://github.com/XiaoYaoNet/Valgrind_Memset

    参考文章:

      valgrind 工具介绍和简单的使用

      Valgrind工具之内存检测

      valgrind 用法详见:http://blog.csdn.net/sduliulun/article/details/7732906

  • 相关阅读:
    array.prototype.slice.call(arguments)
    axios 的坑
    Cannot read property 'range' of null
    IDEA导入Eclipse的JavaEE项目详细步骤链接
    使用 yarn 安装时,报错node_modules ode sass:Command failed.
    axios post请求非json传参设置
    vue-cli 3.x跨域配置
    idea错误: 找不到或无法加载主类
    Git 上传新项目
    Windows下生成SSH密钥
  • 原文地址:https://www.cnblogs.com/ranxf/p/11413735.html
Copyright © 2011-2022 走看看