1.未初始化
下面这个例子使用了一个未初始化的数组
[root@mip-123456 tests]# cat init_mem.c
#include<stdio.h>
int main()
{
int a[2];
if(a[0] == 0)
{
printf("a[0]==0\n");
}
return 0;
}
[root@mip-123456 tests]# vim init_mem.c
[root@mip-123456 tests]# gcc -Wall -o init_mem init_mem.c
[root@mip-123456 tests]# cat init_mem.c
#include<stdio.h>
int main()
{
int a[2];
if(a[0] == 0)
printf("a[0]==0\n");
return 0;
}
[root@mip-123456 tests]# ./init_mem
[root@mip-123456 tests]# gcc -Wall -g -o init_mem init_mem.c
[root@mip-123456 tests]# ./init_mem
[root@mip-123456 tests]# cat init_mem.c
#include<stdio.h>
int main()
{
int a[2];
if(a[0] == 0)
printf("a[0]==0\n");
return 0;
}
[root@mip-123456 tests]# ./init_mem
[root@mip-123456 tests]# valgrind --tool=memcheck --leak-check=yes ./init_mem
==20711== Memcheck, a memory error detector.
==20711== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.
==20711== Using LibVEX rev 1884, a library for dynamic binary translation.
==20711== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.
==20711== Using valgrind-3.4.1, a dynamic binary instrumentation framework.
==20711== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.
==20711== For more details, rerun with: -v
==20711==
==20711== Conditional jump or move depends on uninitialised value(s)
==20711== at 0x804839A: main (init_mem.c:5)
==20711==
==20711== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 13 from 1)
==20711== malloc/free: in use at exit: 0 bytes in 0 blocks.
==20711== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==20711== For counts of detected errors, rerun with: -v
==20711== Use --track-origins=yes to see where uninitialised values come from
==20711== All heap blocks were freed -- no leaks are possible.
[root@mip-123456 tests]# valgrind -v --tool=memcheck --leak-check=yes ./init_mem
==20716== Memcheck, a memory error detector.
==20716== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.
==20716== Using LibVEX rev 1884, a library for dynamic binary translation.
==20716== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.
==20716== Using valgrind-3.4.1, a dynamic binary instrumentation framework.
==20716== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.
==20716==
--20716-- Command line
--20716-- ./init_mem
--20716-- Startup, with flags:
--20716-- -v
--20716-- --tool=memcheck
--20716-- --leak-check=yes
--20716-- Contents of /proc/version:
--20716-- Linux version 2.6.18-92.el5 ([email protected]) (gcc version 4.1.2 20071124 (Red Hat 4.1.2-42)) #1 SMP Tue Jun 10 18:49:47 EDT 2008
--20716-- Arch and hwcaps: X86, x86-sse1-sse2
--20716-- Page sizes: currently 4096, max supported 4096
--20716-- Valgrind library directory: /usr/local/lib/valgrind
--20716-- Reading syms from /lib/ld-2.5.so (0x2de000)
--20716-- Reading syms from /root/Desktop/valgrind-3.4.1/tests/init_mem (0x8048000)
--20716-- Reading syms from /usr/local/lib/valgrind/x86-linux/memcheck (0x38000000)
--20716-- object doesn
内存泄漏
内存泄漏是另外一个常见的问题,也是很多程序中最难判断的问题。内存泄漏的主要表现为:当程序连续运行时,与程序相关的内存(或堆)变得越来越大。结果是, 当这个程序所消耗的内存达到系统的上限时,就会自己崩溃;或者会出现更严重的情况:挂起或导致系统崩溃。下面是一个有内存泄漏 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 运行这个程序时,会返回如下的消息:
清单 5. 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,它试图读写一个超出数组边界的元素。
清单 6. 非法读写
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 }
|
从这个程序中我们可以看出,对于 iw[10] 和 ir[10] 的访问都是非法的,因为 iw 和 ir 都只有 10 个元素,分别是从 0 到 9。请注意 int iw[10 ] 和 iw = (int *)malloc(10*sizeof(int)) 是等效的 —— 它们都是用来给一个整数数组 iw 分配 10 个元素。
当您使用 Valgrind 运行这个程序时,会返回如下的消息:
清单 7. 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。
|