如何使用Valgrind内存检查工具 检查C/C++中内存泄露

来源:互联网 发布:怎样进行商品数据分析 编辑:程序博客网 时间:2024/06/12 01:23
系统编程的一个主要任务是有效地处理内存相关的问题。你的工作越是靠近系统,那么你就就越需要面对内容相关的问题。有时候这些问题非常的致命,很多情况下调试内存相关的问题可能变成恶魔。因此,实际使用中有许多工具可以用于调试内存相关的问题。本文中,我们讨论最流行的开源内存管理框架VALGRIND。来自Valgrind.orgValgrind是一个用于编译动态分析工具的指令框架。它提供一组工具,可以用于执行调试、性能优化或者帮助你改进程序的类似工作。Valgrind架构是模块化的,因此新的工具可以很容易创建,并且不会影响到已有的结构。一些有用的工具作为标准提供。1 memcheck是一个内存错误检测器。它帮助你编译你的程序,特别是让使用C和C++编写的程序更加正确。2 Cachegrind是一个缓存和测试台预测优化工具。它帮助你的程序运行的更快。3 Callgrind是一个调用图生成缓存优化工具。它与Cachegrind有一些重叠,但是也收集一些Cachegrind没有的信息。4 Helgrind是一个线程错误检测器。它帮助你使得多线程程序更加正确。5 DRD也是一个线程错误检测器,它类似于Helgrind,但是使用不同的分析技术,因此可以发现不同的问题。6 Massif是一个堆优化工具,它帮助你让你的程序使用更少的内存。7 DHAT是一个不同类型的堆优化工具,它帮助你发现块生命周期、块使用率和布局的性能低下的问题。8 SGcheck是一个实验性工具,它能检查出栈和全局数组的交叠。它的功能是Memcheck的补充:SGcheck发现Memcheck不能检查的问题,反之亦然。9 BBV是一个实验性SimPoint基本块向量生成器,它提供给处理计算机架构研究和开发人员使用。这里也有一些对许多用户不常使用的工具:Lackey是一个例子工具,说明一些指令的基础;Nulgrind是一个最小Valgrind工具,它不分析或者执行指令,它只用于测试目的。在本文中,我们只是关注于memcheck工具。Memcheck工具的使用如下所示:# valgrind –tool=memcheck ./a.out正如上面命令描述,主要的二进制程序是“Vlagrind”,我们希望使用的工具通过选项”--tool"来指定。“a.out”是一个希望运行mencheck的可执行程序。这个工具可以检查下面的内存相关的问题。1)      没有初始化内存的使用;2)      在释放之后读写内存;3)      在malloc块结束之后读写内存;4)      内存泄露5)      Malloc/new/new[]和free/delete/delete[]不配对使用;6)      两次释放内存。注意:上面的列表不全面,但是包括这个工具所能检查的流行问题。让我们逐一讨论上面的场景:注意:下面描述的所有的测试代码都使用带有-g选项时能的gcc进行编译(为了在memcheck输出中国生成行号)。正如前面早些时候讨论的《C程序编译成可执行程序》,它必须通过4个不同的阶段。1 没有初始化内存的使用代码:#include <stdio.h>#include <stdlib.h> int main(void) {   char *p;   char c = *p;   printf("\n [%c]\n",c);   return 0;}在上面的代码中,我们试图使用没有初始化的指针“p”。让我们运行memcheck并且查看下结果。#  gcc -g val.c -o val# # valgrind --tool=memcheck ./val==3087== Memcheck, a memory error detector==3087== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==3087== Using Valgrind-3.5.0 and LibVEX;rerun with -h for copyright info==3087== Command: ./val==3087== ==3087== Use of uninitialised value of size8==3087==    at 0x4004A4: main (val.c:8)==3087==   []==3087== ==3087== HEAP SUMMARY:==3087==    in use at exit: 0 bytes in 0 blocks==3087==  total heap usage: 0 allocs, 0 frees, 0 bytes allocated==3087== ==3087== All heap blocks were freed -- noleaks are possible==3087== ==3087== For counts of detected andsuppressed errors, rerun with: -v==3087== Use --track-origins=yes to seewhere uninitialised values come from==3087== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)正如上面输出所示,Valgrind检查到没有初始化变量,并且给出警告(查看上面粗体行)。2 在内存释放之后读写内存代码:#include <stdio.h>#include <stdlib.h>  int main(void){   char *p = malloc(1);   *p = 'a';     char c = *p;     printf("\n [%c]\n",c);     free(p);    c= *p;   return 0;}在上面的代码片段中,我们释放指针“p”并且又试图访问这个指针的变量值。让我们运行memcheck,查看valgrind必须提供的场景。valgrind --tool=memcheck ./val2==3106== Memcheck, a memory error detector==3106== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==3106== Using Valgrind-3.5.0 and LibVEX;rerun with -h for copyright info==3106== Command: ./val2==3106==   [a]==3106== Invalid read of size 1==3106==    at 0x40056F: main (val2.c:14)==3106==  Address 0x4c25040 is 0 bytes inside a blockof size 1 free'd==3106==    at 0x4A05A31: free (vg_replace_malloc.c:325)==3106==    by 0x40056A: main (val2.c:13)==3106== ==3106== ==3106== HEAP SUMMARY:==3106==    in use at exit: 0 bytes in 0 blocks==3106==  total heap usage: 1 allocs, 1 frees, 1 bytes allocated==3106== ==3106== All heap blocks were freed -- noleaks are possible==3106== ==3106== For counts of detected andsuppressed errors, rerun with: -v==3106== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)正如上面所示,工具检测到不可用的读取,并且打印出警告“Invalid read of size 1”。3 读写malloc块结束之后的内存代码:#include <stdio.h>#include <stdlib.h>  int main(void){   char *p = malloc(1);   *p = 'a';     char c = *(p+1);     printf("\n [%c]\n",c);     free(p);   return 0;}上面的代码片段中,我们已经分配1个字节的“p”,但是当读取指到“c”时,我们访问地址p+1。现在我们针对这段代码运行valgrind。$ valgrind --tool=memcheck ./val==2835== Memcheck, a memory error detector==2835== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==2835== Using Valgrind-3.6.0.SVN-Debianand LibVEX; rerun with -h for copyright info==2835== Command: ./val==2835====2835== Invalid read of size 1==2835==   at 0x4005D9: main (valgrind.c:25)==2835== Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd==2835==   at 0x4C274A8: malloc (vg_replace_malloc.c:236)==2835==   by 0x4005C5: main (valgrind.c:22)==2835==   []==2835====2835== HEAP SUMMARY:==2835==    in use at exit: 0 bytes in 0 blocks==2835==  total heap usage: 1 allocs, 1 frees, 1 bytes allocated==2835====2835== All heap blocks were freed -- noleaks are possible==2835====2835== For counts of detected andsuppressed errors, rerun with: -v==2835== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)在这个实例中,工具也检查到执行不可用的读取。4 内存泄露代码:#include <stdio.h>#include <stdlib.h>  int main(void){   char *p = malloc(1);   *p = 'a';    char c = *p;    printf("\n [%c]\n",c);    return 0;}在这个代码中,我们分配一个字节,但是没有释放它,现在让我们运行valgrind看看发生什么。$ valgrind --tool=memcheck--leak-check=full ./val==2888== Memcheck, a memory error detector==2888== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==2888== Using Valgrind-3.6.0.SVN-Debianand LibVEX; rerun with -h for copyright info==2888== Command: ./val==2888==   [a]==2888====2888== HEAP SUMMARY:==2888==    in use at exit: 1 bytes in 1 blocks==2888==  total heap usage: 1 allocs, 0 frees, 1 bytes allocated==2888====2888==1 bytes in 1 blocks are definitely lost in loss record 1 of 1==2888==    at 0x4C274A8: malloc(vg_replace_malloc.c:236)==2888==    by 0x400575: main (valgrind.c:6)==2888====2888== LEAK SUMMARY:==2888==   definitely lost: 1 bytes in 1 blocks==2888==   indirectly lost: 0 bytes in 0 blocks==2888==      possibly lost: 0 bytes in 0 blocks==2888==   still reachable: 0 bytes in 0 blocks==2888==         suppressed: 0 bytes in 0 blocks==2888====2888== For counts of detected andsuppressed errors, rerun with: -v==2888== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)上面粗体行显示工具能够检测到内存泄露。注意:在这种情况下我们添加扩展选项“—leak-check=full”获得内存泄露的详情。5 malloc/new/new[]和free/delete/delete[]不匹配代码:#include <stdio.h>#include <stdlib.h>#include<iostream>  int main(void){   char *p = (char*)malloc(1);   *p = 'a';     char c = *p;     printf("\n [%c]\n",c);   delete p;   return 0;}在上面的代码中,我们使用malloc分配一个内存,但是使用delete操作删除内存。注意:使用g++编译上面的代码,delete操作只有在C++中使用,g++用来编译c++代码。让我们运行工具,结果如下:$ valgrind --tool=memcheck--leak-check=full ./val==2972== Memcheck, a memory error detector==2972== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==2972== Using Valgrind-3.6.0.SVN-Debianand LibVEX; rerun with -h for copyright info==2972== Command: ./val==2972==   [a]==2972== Mismatched free() / delete /delete []==2972==   at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387)==2972==   by 0x40080B: main (valgrind.c:13)==2972== Address 0x595e040 is 0 bytes inside a block of size 1 alloc'd==2972==   at 0x4C274A8: malloc (vg_replace_malloc.c:236)==2972==   by 0x4007D5: main (valgrind.c:7)==2972====2972====2972== HEAP SUMMARY:==2972==    in use at exit: 0 bytes in 0 blocks==2972==  total heap usage: 1 allocs, 1 frees, 1 bytes allocated==2972====2972== All heap blocks were freed -- noleaks are possible==2972====2972== For counts of detected andsuppressed errors, rerun with: -v==2972== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)我们从上面输出看到(参见粗体行),工具清楚的说明“Mismatched free()/delete/delete[]”。我们试图在测试代码中使用new和free的组合,并且看到工具给出的结果。6 两次释放内存代码:#include <stdio.h>#include <stdlib.h>  int main(void){   char *p = (char*)malloc(1);   *p = 'a';     char c = *p;   printf("\n [%c]\n",c);   free(p);   free(p);   return 0;}上面代码片段中,我们释放内存指针“p”两次,现在让我们运行memcheck工具。$ valgrind --tool=memcheck--leak-check=full ./val==3167== Memcheck, a memory error detector==3167== Copyright (C) 2002-2009, and GNUGPL'd, by Julian Seward et al.==3167== Using Valgrind-3.6.0.SVN-Debianand LibVEX; rerun with -h for copyright info==3167== Command: ./val==3167==   [a]==3167== Invalid free() / delete / delete[]==3167==   at 0x4C270BD: free (vg_replace_malloc.c:366)==3167==   by 0x40060A: main (valgrind.c:12)==3167== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd==3167==   at 0x4C270BD: free (vg_replace_malloc.c:366)==3167==   by 0x4005FE: main (valgrind.c:11)==3167====3167====3167== HEAP SUMMARY:==3167==    in use at exit: 0 bytes in 0 blocks==3167==  total heap usage: 1 allocs, 2 frees, 1 bytes allocated==3167====3167== All heap blocks were freed -- noleaks are possible==3167====3167== For counts of detected andsuppressed errors, rerun with: -v==3167== ERROR SUMMARY: 1 errors from 1contexts (suppressed: 4 from 4)如上面输出看到,工具检测到我们在相同的指针上调用了两次free。在本文中,我们关注于内存管理框架valgrind和使用memcheck工具(该框架提供的)来说明如何让接触内存的开发工作更加的容易。该工具能够检测许多内存相关的问题,它们很难被人工发现的,我们可以借助于valgrind内存检查工具。