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

系统编程的一个主要任务是有效地处理内存相关的问题。你的工作越是靠近系统,那么你就就越需要面对内容相关的问题。
有时候这些问题非常的致命,很多情况下调试内存相关的问题可能变成恶魔。因此,实际使用中有许多工具可以用于调试内存相关的问题。
本文中,我们讨论最流行的开源内存管理框架VALGRIND。
来自Valgrind.org Valgrind是一个用于编译动态分析工具的指令框架。它提供一组工具,可以用于执行调试、性能优化或者帮助你改进程序的类似工作。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内存检查工具。

你可能感兴趣的:(valgrind)