Chinaunix首页 | 论坛 | 博客
  • 博客访问: 391613
  • 博文数量: 63
  • 博客积分: 3142
  • 博客等级: 中校
  • 技术积分: 838
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-06 13:35
文章分类

全部博文(63)

文章存档

2011年(2)

2010年(114)

2009年(3)

我的朋友

分类: LINUX

2010-08-31 13:21:49

Linux下用Valgrind防止内存泄露 用C/C++开发其中最令人头疼的一个问题就是内存管理,有时候为了查找一个内存泄漏或者一个内存访问越界,需要要花上好几天时间,如果有一款工具能够帮助我们做这件事情就好了,valgrind正好就是这样的一款工具。

Valgrind是一款基于模拟linux下的程序调试器和剖析器的软件套件,可以运行于x86, amd64和ppc32架构上。valgrind包含一个核心,它提供一个虚拟的CPU运行程序,还有一系列的工具,它们完成调试,剖析和一些类似的任务。valgrind是高度模块化的,所以开发人员或者用户可以给它添加新的工具而不会损坏己有的结构。

valgrind的官方网址是:

可以在它的网站上下载到最新的valgrind,它是开放源码和免费的。

一、介绍

valgrind包含几个标准的工具,它们是:

1、memcheck

memcheck探测程序中内存管理存在的问题。它检查所有对内存的读/写操作,并截取所有的malloc/new/free/delete调用。因此memcheck工具能够探测到以下问题:

1)使用未初始化的内存

2)读/写已经被释放的内存

3)读/写内存越界

4)读/写不恰当的内存栈空间

5)内存泄漏

6)使用malloc/new/new[]和free/delete/delete[]不匹配。

2、cachegrind
cachegrind是一个cache剖析器。它模拟执行CPU中的L1, D1和L2 cache,因此它能很精确的指出代码中的cache未命中。如果你需要,它可以打印出cache未命中的次数,内存引用和发生cache未命中的每一行代码,每一个函数,每一个模块和整个程序的摘要。如果你要求更细致的信息,它可以打印出每一行机器码的未命中次数。在x86和amd64上,cachegrind通过CPUID自动探测机器的cache配置,所以在多数情况下它不再需要更多的配置信息了。

3、helgrind

helgrind查找多线程程序中的竞争数据。helgrind查找内存地址,那些被多于一条线程访问的内存地址,但是没有使用一致的锁就会被查出。这表示这些地址在多线程间访问的时候没有进行同步,很可能会引起很难查找的时序问题。

二、valgrind对你的程序都做了些什么

valgrind被设计成非侵入式的,它直接工作于可执行文件上,因此在检查前不需要重新编译、连接和修改你的程序。要检查一个程序很简单,只需要执行下面的命令就可以了

valgrind –tool=tool_name program_name

比如我们要对ls -l命令做内存检查,只需要执行下面的命令就可以了

valgrind –tool=memcheck ls -l

不管是使用哪个工具,valgrind在开始之前总会先取得对你的程序的控制权,从可执行关联库里读取调试信息。然后在valgrind核心提供的虚拟CPU上运行程序,valgrind会根据选择的工具来处理代码,该工具会向代码中加入检测代码,并把这些代码作为最终代码返回给valgrind核心,最后valgrind核心运行这些代码。

如果要检查内存泄漏,只需要增加–leak-check=yes就可以了,命令如下

valgrind –tool=memcheck –leak-check=yes ls -l

不同工具间加入的代码变化非常的大。在每个作用域的末尾,memcheck加入代码检查每一片内存的访问和进行值计算,代码大小至少增加12倍,运行速度要比平时慢25到50倍。

valgrind模拟程序中的每一条指令执行,因此,检查工具和剖析工具不仅仅是对你的应用程序,还有对共享库,GNU C库,X的客户端库都起作用。

三、现在开始

下载以后,解压文件

tar xvf valgrind-3.5.0.tar.bz2
cd valgrind-3.5.0
./configure  –prefix=/where/you/want/it/installed
make
make install

然后:测试程序。
首先,在编译程序的时候打开调试模式(gcc编译器的-g选项)。如果没有调试信息,即使最好的valgrind工具也将中能够猜测特定的代码是属于哪一个函数。打开调试选项进行编译后再用valgrind检查,valgrind将会给你的个详细的报告,比如哪一行代码出现了内存泄漏。

当检查的是C++程序的时候,还应该考虑另一个选项 -fno-inline。它使得函数调用链很清晰,这样可以减少你在浏览大型C++程序时的混乱。比如在使用这个选项的时候,用memcheck检查openoffice就很容易。当然,你可能不会做这项工作,但是使用这一选项使得valgrind生成更精确的错误报告和减少混乱。

一些编译优化选项(比如-O2或者更高的优化选项),可能会使得memcheck提交错误的未初始化报告,因此,为了使得valgrind的报告更精确,在编译的时候最好不要使用优化选项。

如果程序是通过脚本启动的,可以修改脚本里启动程序的代码,或者使用–trace-children=yes选项来运行脚本。

下面是用memcheck检查ls -l命令的输出报告,在终端下执行下面的命令

valgrind –tool=memcheck ls -l

程序会打印出ls -l命令的结果,最后是valgrind的检查报告如下:

==8439== Memcheck, a memory error detector.
==8439== Copyright (C) 2002-2006, and GNU GPL’d, by Julian Seward et al.
==8439== Using LibVEX rev 1658, a library for dynamic binary translation.
==8439== Copyright (C) 2004-2006, and GNU GPL’d, by OpenWorks LLP.
==8439== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
==8439== Copyright (C) 2000-2006, and GNU GPL’d, by Julian Seward et al.
==8439== For more details, rerun with: -v
==8439==
==8439== Invalid write of size 4
==8439==    at 0×4004C6: main (11.c:6)
==8439==  Address 0×4C2F038 is 8 bytes inside a block of size 10 alloc’d
==8439==    at 0×4A05809: malloc (vg_replace_malloc.c:149)
==8439==    by 0×4004B0: main (11.c:5)
==8439==
==8439== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==8439== malloc/free: in use at exit: 10 bytes in 1 blocks.
==8439== malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
==8439== For counts of detected errors, rerun with: -v
==8439== searching for pointers to 1 not-freed blocks.
==8439== checked 63,528 bytes.
==8439==
==8439==
==8439== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8439==    at 0×4A05809: malloc (vg_replace_malloc.c:149)
==8439==    by 0×4004B0: main (11.c:5)
==8439==
==8439== LEAK SUMMARY:
==8439==    definitely lost: 10 bytes in 1 blocks.
==8439==      possibly lost: 0 bytes in 0 blocks.
==8439==    still reachable: 0 bytes in 0 blocks.
==8439==         suppressed: 0 bytes in 0 blocks.
==8439== Reachable blocks (those to which a pointer was found) are not shown.
==8439== To see them, rerun with: –show-reachable=yes

这里的“8439”指的是执行ls -l的进程ID,这有利于区别不同进程的报告。memcheck会给出报告,分配置和释放了多少内存,有多少内存泄漏了,还有多少内存的访问是可达的,检查了多少字节的内存。
下面举两个用valgrind做内存检查的例子
例子一 (11.c):

#include
int main(int argc, char *argv[])
{
    char *ptr;
    ptr = (char*) malloc(10);
    strcpy(ptr, “01234567890″);
    return 0;
}

编译程序

gcc -g -o 11 11.c

用valgrind执行命令

valgrind –tool=memcheck –leak-check=yes ./11

报告如下

==8427== Memcheck, a memory error detector.
==8427== Copyright (C) 2002-2006, and GNU GPL’d, by Julian Seward et al.
==8427== Using LibVEX rev 1658, a library for dynamic binary translation.
==8427== Copyright (C) 2004-2006, and GNU GPL’d, by OpenWorks LLP.
==8427== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
==8427== Copyright (C) 2000-2006, and GNU GPL’d, by Julian Seward et al.
==8427== For more details, rerun with: -v
==8427==
==8427== Invalid write of size 4
==8427==    at 0×4004C6: main (in /root/11)
==8427==  Address 0×4C2F038 is 8 bytes inside a block of size 10 alloc’d
==8427==    at 0×4A05809: malloc (vg_replace_malloc.c:149)
==8427==    by 0×4004B0: main (in /root/11)
==8427==
==8427== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 1)
==8427== malloc/free: in use at exit: 10 bytes in 1 blocks.
==8427== malloc/free: 1 allocs, 0 frees, 10 bytes allocated.
==8427== For counts of detected errors, rerun with: -v
==8427== searching for pointers to 1 not-freed blocks.
==8427== checked 63,528 bytes.
==8427==
==8427==
==8427== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8427==    at 0×4A05809: malloc (vg_replace_malloc.c:149)
==8427==    by 0×4004B0: main (in /root/11)
==8427==
==8427== LEAK SUMMARY:
==8427==    definitely lost: 10 bytes in 1 blocks.
==8427==      possibly lost: 0 bytes in 0 blocks.
==8427==    still reachable: 0 bytes in 0 blocks.
==8427==         suppressed: 0 bytes in 0 blocks.
==8427== Reachable blocks (those to which a pointer was found) are not shown.
==8427== To see them, rerun with: –show-reachable=yes

从这份报告可以看出,进程号是8427,test.c的第8行写内存越界了,引起写内存越界的是strcpy函数,
第7行泄漏了10个字节的内存,引起内存泄漏的是malloc函数。
例子二(test2.c)

#include
int foo(int x)
{
    if (x < 0) {
        printf(“%d “, x);
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int x;  
    foo(x);
    return 0;
}

编译程序

gcc -g -o test2 test2.c

用valgrind做内存检查

valgrind –tool=memcheck ./test2

输出报告如下

==4285== Memcheck, a memory error detector.
==4285== Copyright (C) 2002-2006, and GNU GPL’d, by Julian Seward et al.
==4285== Using LibVEX rev 1606, a library for dynamic binary translation.
==4285== Copyright (C) 2004-2006, and GNU GPL’d, by OpenWorks LLP.
==4285== Using valgrind-3.2.0, a dynamic binary instrumentation framework.
==4285== Copyright (C) 2000-2006, and GNU GPL’d, by Julian Seward et al.
==4285== For more details, rerun with: -v
==4285==
==4285== Conditional jump or move depends on uninitialised value(s)
==4285== at 0×8048372: foo (test2.c:5)
==4285== by 0×80483B4: main (test2.c:16)
==4285==p p
==4285== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 12 from 1)
==4285== malloc/free: in use at exit: 0 bytes in 0 blocks.
==4285== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==4285== For counts of detected errors, rerun with: -v
==4285== All heap blocks were freed — no leaks are possible.

从这份报告可以看出进程PID是4285,test2.c文件的第16行调用了foo函数,在test2.c文件的第5行foo函数使用了一个未初始化的变量。
valgrind还有很多使用选项,具体可以查看valgrind的man手册页和valgrind官方网站的在线文档。

------------------------------------------------------------------------------------

Valgrind 已经在 Linux 应用程序开发社区中广泛用来调试应用程序。它尤其擅长发现内存管理的问题。它可以检查程序运行时的内存泄漏问题。这个工具目前正由 Julian Seward 进行开发,并由 Paul Mackerras 移植到了 Power 架构上。

要安装 Valgrind,请从 Valgrind 的 Web 站点上下载源代码(参阅 参考资料)。切换到 Valgrind 目录,并执行下面的命令:

# make
# make check
# make install

Valgrind 的输出格式如下:




# valgrind du –x –s
.
.
==29404== Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'd
==29404== at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==29404== by 0xFEE1AD0: strdup (in /lib/tls/libc.so.6)
==29404== by 0xFE94D30: setlocale (in /lib/tls/libc.so.6)
==29404== by 0x10001414: main (in /usr/bin/du)

de>==29404==de> 是进程的 ID。消息 de>Address 0x1189AD84 is 0 bytes after a block of size 12 alloc'dde> 说明在这个 12 字节的数组后面没有存储空间了。第二行以及后续几行说明内存是在 130 行(vg_replace_malloc.c)的 de>strdup()de> 程序中进行分配的。de>strdup()de> 是在 libc.so.6 库的 de>setlocale()de> 中调用的;de>main()de> 调用了 de>setlocale()de>。

最为常见的一个 bug 是程序使用了未初始化的内存。未初始化的数据可能来源于:

  • 未经初始化的变量
  • malloc 函数所分配的数据,在写入值之前使用了

下面这个例子使用了一个未初始化的数组:




2 {
3 int i[5];
4
5 if (i[0] == 0)
6 i[1]=1;
7 return 0;
8 }

在这个例子中,整数数组 i[5] 没有进行初始化;因此,i[0] 包含的是一个随机数。因此使用 i[0] 的值来判断一个条件分支就会导致不可预期的问题。Valgrind 可以很容易捕获这种错误条件。当您使用 Valgrind 运行这个程序时,就会接收到下面的消息:




# gcc –g –o test1 test1.c
# valgrind ./test1
.
.
==31363==
==31363== Conditional jump or move depends on uninitialised value(s)
==31363== at 0x1000041C: main (test1.c:5)
==31363==
==31363== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
==31363== malloc/free: in use at exit: 0 bytes in 0 blocks.
==31363== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==31363== For counts of detected errors, rerun with: -v
==31363== No malloc'd blocks -- no leaks are possible.

Valgrind 的输出说明,有一个条件分支依赖于文件 test1.c 中第 5 行中的一个未初始化的变量。

内存泄漏是另外一个常见的问题,也是很多程序中最难判断的问题。内存泄漏的主要表现为:当程序连续运行时,与程序相关的内存(或堆)变得越来越大。结果是,当这个程序所消耗的内存达到系统的上限时,就会自己崩溃;或者会出现更严重的情况:挂起或导致系统崩溃。下面是一个有内存泄漏 bug 的示例程序:




1 int main(void)
2 {
3 char *p1;
4 char *p2;
5
6 p1 = (char *) malloc(512);
7 p2 = (char *) malloc(512);
8
9 p1=p2;
10
11 free(p1);
12 free(p2);
13 }

上面的代码分别给字符指针 p1 和 p2 分配了两个 512 字节的内存块,然后将指向第一个内存块的指针设置为指向第二个内存块。结果是,第二个内存块的地址丢失了,并导致内存泄漏。在使用 Valgrind 运行这个程序时,会返回如下的消息:




# gcc –g –o test2 test2.c
# valgrind ./test2
.
.
==31468== Invalid free() / delete / delete[]
==31468== at 0xFFB9FF0: free (vg_replace_malloc.c:152)
==31468== by 0x100004B0: main (test2.c:12)
==31468== Address 0x11899258 is 0 bytes inside a block of size 512 free'd
==31468== at 0xFFB9FF0: free (vg_replace_malloc.c:152)
==31468== by 0x100004A4: main (test2.c:11)
==31468==
==31468== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 7 from 1)
==31468== malloc/free: in use at exit: 512 bytes in 1 blocks.
==31468== malloc/free: 2 allocs, 2 frees, 1024 bytes allocated.
==31468== For counts of detected errors, rerun with: -v
==31468== searching for pointers to 1 not-freed blocks.
==31468== checked 167936 bytes.
==31468==
==31468== LEAK SUMMARY:
==31468== definitely lost: 512 bytes in 1 blocks.
==31468== possibly lost: 0 bytes in 0 blocks.
==31468== still reachable: 0 bytes in 0 blocks.
==31468== suppressed: 0 bytes in 0 blocks.
==31468== Use --leak-check=full to see details of leaked memory.

正如您可以看到的一样,Valgrind 报告说这个程序中有 512 字节的内存丢失了。

这种情况发生在程序试图对一个不属于程序本身的内存地址进行读写时。在有些系统上,在发生这种错误时,程序会异常结束,并产生一个段错误。下面这个例子就是一个常见的 bug,它试图读写一个超出数组边界的元素。




1 int main() {
2 int i, *iw, *ir;
3
4 iw = (int *)malloc(10*sizeof(int));
5 ir = (int *)malloc(10*sizeof(int));
6
7
8 for (i=0; i<11; i++)
9 iw[i] = i;
10
11 for (i=0; i<11; i++)
12 ir[i] = iw[i];
13
14 free(iw);
15 free(ir);
16 }

从这个程序中我们可以看出,对于 de>iw[10]de> 和 de>ir[10]de> 的访问都是非法的,因为 de>iwde> 和 de>irde> 都只有 10 个元素,分别是从 0 到 9。请注意 de>int iw[10 ]de> 和 de>iw = (int *)malloc(10*sizeof(int))de> 是等效的 —— 它们都是用来给一个整数数组 iw 分配 10 个元素。

当您使用 Valgrind 运行这个程序时,会返回如下的消息:




# gcc –g –o test3 test3.c
# valgrind ./test3
.
.
==31522== Invalid write of size 4
==31522== at 0x100004C0: main (test3.c:9)
==31522== Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
==31522== at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==31522== by 0x10000474: main (test10.c:4)
==31522==
==31522== Invalid read of size 4
==31522== at 0x1000050C: main (test3.c:12)
==31522== Address 0x11899050 is 0 bytes after a block of size 40 alloc'd
==31522== at 0xFFB9964: malloc (vg_replace_malloc.c:130)
==31522== by 0x10000474: main (test10.c:4)
==31522==
==31522== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 7 from 1)
==31522== malloc/free: in use at exit: 0 bytes in 0 blocks.
==31522== malloc/free: 2 allocs, 2 frees, 84 bytes allocated.
==31522== For counts of detected errors, rerun with: -v
==31522== No malloc'd blocks -- no leaks are possible.

在 test3.c 的第 9 行发现一个非法的 4 字节写操作,在第 12 行发现一个非法的 4 字节读操作。

Valgrind 也可以帮助判断内存误用的问题,例如:

  • 读/写已经释放的内存
  • C++ 环境中错误地使用 malloc/new 与 free/delete 的配对

下面这个列表介绍了 POWER 架构上 Valgrind 的状态:

  • memcheck 和 addrcheck 工具都可以很好地工作。然而,其他工具还没有进行大量的测试。另外,Helgrind (一个数据竞争的检测程序)在 POWER 上尚不能使用。
  • 所有的 32 位 PowerPC? 用户模式的指令都可以支持,除了两条非常少用的指令:lswx 和 stswx。具体来说,所有的浮点和 Altivec(VMX)指令都可以支持。
  • Valgrind 可以在 32 位或 64 位 PowerPC/Linux 内核上工作,但是只能用于 32 位的可执行程序。

有关 Valgrind 内存调试的更多信息,请访问 Valgrind HOW TO 站点。还可以参阅 Steve Best 的“Debugging Memory Problems”(Linux Magazine,2003 年 5 月)。参考资料 中有它们的链接

除了 Valgrind 之外,还可以使用其他几个内存调试工具;例如,Memwatch 和 Electric Fence。

阅读(2868) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-09-02 08:12:47

Download More than 1000 free IT eBooks: http://free-ebooks.appspot.com