Valgrind 例子
Posted 啃萝卜的兔子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Valgrind 例子相关的知识,希望对你有一定的参考价值。
检测内存泄漏
#include <stdlib.h> #include <stdio.h> int main(void) { char *ptr; ptr = (char *)malloc(10); return 0; }
保存为memleak.c并编译,然后用valgrind检测。
$ gcc -o memleak memleak.c
(valgrind和purify最大的不同在于:valgrind只接管程序执行的过程,编译时不需要valgrind干预,而purify会干预程序编译过程)
$ valgrind --tool=memcheck ./memleak
我们得到如下错误信息:
[konten@tencent test_valgrind]$ valgrind ./memleak ==29646== Memcheck, a memory error detector. ==29646== Copyright (C) 2002-2007, and GNU GPL\'d, by Julian Seward et al. ==29646== Using LibVEX rev 1732, a library for dynamic binary translation. ==29646== Copyright (C) 2004-2007, and GNU GPL\'d, by OpenWorks LLP. ==29646== Using valgrind-3.2.3, a dynamic binary instrumentation framework. ==29646== Copyright (C) 2000-2007, and GNU GPL\'d, by Julian Seward et al. ==29646== For more details, rerun with: -v ==29646== ==29646== ==29646== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 1) ==29646== malloc/free: in use at exit: 10 bytes in 1 blocks. //指示在程序退出时,还有多少内存没有释放。 ==29646== malloc/free: 1 allocs, 0 frees, 10 bytes allocated. // 指示该执行过程malloc和free调用的次数。 ==29646== For counts of detected errors, rerun with: -v // 提示如果要更详细的信息,用-v选项。 ==29646== searching for pointers to 1 not-freed blocks. ==29646== checked 56,164 bytes. ==29646== ==29646== LEAK SUMMARY: ==29646== definitely lost: 10 bytes in 1 blocks. ==29646== possibly lost: 0 bytes in 0 blocks. ==29646== still reachable: 0 bytes in 0 blocks. ==29646== suppressed: 0 bytes in 0 blocks. ==29646== Rerun with --leak-check=full to see details of leaked memory. [konten@tencent test_valgrind]$
可以看到,如果我们仅仅用默认方式执行,valgrind只报告内存泄漏,但没有显示具体代码中泄漏的地方。
因此我们需要使用 “--leak-check=full”选项启动 valgrind,我们再执行一次:
[konten@tencent test_valgrind]$ valgrind --leak-check=full ./memleak ==29661== Memcheck, a memory error detector. ==29661== Copyright (C) 2002-2007, and GNU GPL\'d, by Julian Seward et al. ==29661== Using LibVEX rev 1732, a library for dynamic binary translation. ==29661== Copyright (C) 2004-2007, and GNU GPL\'d, by OpenWorks LLP. ==29661== Using valgrind-3.2.3, a dynamic binary instrumentation framework. ==29661== Copyright (C) 2000-2007, and GNU GPL\'d, by Julian Seward et al. ==29661== For more details, rerun with: -v ==29661== ==29661== ==29661== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 15 from 1) ==29661== malloc/free: in use at exit: 10 bytes in 1 blocks. ==29661== malloc/free: 1 allocs, 0 frees, 10 bytes allocated. ==29661== For counts of detected errors, rerun with: -v ==29661== searching for pointers to 1 not-freed blocks. ==29661== checked 56,164 bytes. ==29661== ==29661== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==29661== at 0x401A846: malloc (vg_replace_malloc.c:149) ==29661== by 0x804835D: main (memleak.c:6) ==29661== ==29661== LEAK SUMMARY: ==29661== definitely lost: 10 bytes in 1 blocks. ==29661== possibly lost: 0 bytes in 0 blocks. ==29661== still reachable: 0 bytes in 0 blocks. ==29661== suppressed: 0 bytes in 0 blocks. [konten@tencent test_valgrind]$
和上次的执行结果基本相同,只是指明了代码中出现泄漏的具体位置。
#include <stdlib.h> int main() { char *x = (char*)malloc(20); char *y = (char*)malloc(20); x=y; free(x); free(y); return 0; } Valgrind提示如下 ==19013== Invalid free() / delete / delete[] ==19013== at 0x4A0541E: free (vg_replace_malloc.c:233) ==19013== by 0x4004F5: main (sample5.c:8) ==19013== Address 0x4C2E078 is 0 bytes inside a block of size 20 free\'d ==19013== at 0x4A0541E: free (vg_replace_malloc.c:233) ==19013== by 0x4004EC: main (sample5.c:7) ==19013== ==19013== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1) ==19013== malloc/free: in use at exit: 20 bytes in 1 blocks. ==19013== malloc/free: 2 allocs, 2 frees, 40 bytes allocated. ==19013== For counts of detected errors, rerun with: -v ==19013== searching for pointers to 1 not-freed blocks. ==19013== checked 66,584 bytes. ==19013== ==19013== LEAK SUMMARY: ==19013== definitely lost: 20 bytes in 1 blocks. ==19013== possibly lost: 0 bytes in 0 blocks. ==19013== still reachable: 0 bytes in 0 blocks. ==19013== suppressed: 0 bytes in 0 blocks. ==19013== Use --leak-check=full to see details of leaked memory.
使用未初始化的内存
#include <stdio.h> int main() { int x; if(x == 0) { printf("X is zero"); } return 0; } Valgrind提示如下 ==14222== Conditional jump or move depends on uninitialised value(s) ==14222== at 0x400484: main (sample2.c:6) X is zero==14222== ==14222== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1) ==14222== malloc/free: in use at exit: 0 bytes in 0 blocks. ==14222== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==14222== For counts of detected errors, rerun with: -v ==14222== All heap blocks were freed -- no leaks are possible.
内存读写越界
#include <stdlib.h> #include <stdio.h> int main(int argc,char *argv[]) { int len=5; int i; int *pt=(int*)malloc(len*sizeof(int)); int *p=pt; for(i=0;i<len;i++) {p++;} *p=5; printf(“%d”,*p); return; } Valgrind提示如下 ==23045== Invalid write of size 4 ==23045== at 0x40050A: main (sample2.c:11) ==23045== Address 0x4C2E044 is 0 bytes after a block of size 20 alloc\'d ==23045== at 0x4A05809: malloc (vg_replace_malloc.c:149) ==23045== by 0x4004DF: main (sample2.c:7) ==23045== ==23045== Invalid read of size 4 ==23045== at 0x400514: main (sample2.c:12) ==23045== Address 0x4C2E044 is 0 bytes after a block of size 20 alloc\'d ==23045== at 0x4A05809: malloc (vg_replace_malloc.c:149) ==23045== by 0x4004DF: main (sample2.c:7) 5==23045== ==23045== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 5 from 1) ==23045== malloc/free: in use at exit: 20 bytes in 1 blocks. ==23045== malloc/free: 1 allocs, 0 frees, 20 bytes allocated. ==23045== For counts of detected errors, rerun with: -v ==23045== searching for pointers to 1 not-freed blocks. ==23045== checked 66,584 bytes. ==23045== ==23045== LEAK SUMMARY: ==23045== definitely lost: 20 bytes in 1 blocks. ==23045== possibly lost: 0 bytes in 0 blocks. ==23045== still reachable: 0 bytes in 0 blocks. ==23045== suppressed: 0 bytes in 0 blocks. ==23045== Use --leak-check=full to see details of leaked memory.
src和dst内存覆盖
#include <stdlib.h> #include <stdio.h> #include <string.h> int main(int argc,char *argv[]) { char x[50]; int i; for(i=0;i<50;i++) {
x[i]=i;
} strncpy(x+20,x,20); //Good strncpy(x+20,x,21); //Overlap x[39]=’\\0’; strcpy(x,x+20); //Good x[39]=40; x[40]=’\\0’; strcpy(x,x+20); //Overlap return 0; } Valgrind提示如下 ==24139== Source and destination overlap in strncpy(0x7FEFFFC09, 0x7FEFFFBF5, 21) ==24139== at 0x4A0724F: strncpy (mc_replace_strmem.c:116) ==24139== by 0x400527: main (sample3.c:10) ==24139== ==24139== Source and destination overlap in strcpy(0x7FEFFFBE0, 0x7FEFFFBF4) ==24139== at 0x4A06E47: strcpy (mc_replace_strmem.c:106) ==24139== by 0x400555: main (sample3.c:15) ==24139== ==24139== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 5 from 1) ==24139== malloc/free: in use at exit: 0 bytes in 0 blocks. ==24139== malloc/free: 0 allocs, 0 frees, 0 bytes allocated. ==24139== For counts of detected errors, rerun with: -v ==24139== All heap blocks were freed -- no leaks are possible.
动态内存管理错误
常见的内存分配方式分三种:静态存储,栈上分配,堆上分配。全局变量属于静态存储,它们是在编译时就被分配了存储空间,函数内的局部变量属于栈上分配,而最灵活的内存使用方式当属堆上分配,也叫做内存动态分配了。常用的内存动态分配函数包括:malloc, alloc, realloc, new等,动态释放函数包括free, delete。
一旦成功申请了动态内存,我们就需要自己对其进行内存管理,而这又是最容易犯错误的。常见的内存动态管理错误包括:
1. 申请和释放不一致
由于 C++ 兼容 C,而 C 与 C++ 的内存申请和释放函数是不同的,因此在 C++ 程序中,就有两套动态内存管理函数。一条不变的规则就是采用 C 方式申请的内存就用 C 方式释放;用 C++ 方式申请的内存,用 C++ 方式释放。也就是用 malloc/alloc/realloc 方式申请的内存,用 free 释放;用 new 方式申请的内存用 delete 释放。在上述程序中,用 malloc 方式申请了内存却用 delete 来释放,虽然这在很多情况下不会有问题,但这绝对是潜在的问题。
2. 申请和释放不匹配
申请了多少内存,在使用完成后就要释放多少。如果没有释放,或者少释放了就是内存泄露;多释放了也会产生问题。上述程序中,指针p和pt指向的是同一块内存,却被先后释放两次。
3.释放后仍然读写
本质上说,系统会在堆上维护一个动态内存链表,如果被释放,就意味着该块内存可以继续被分配给其他部分,如果内存被释放后再访问,就可能覆盖其他部分的信息,这是一种严重的错误,上述程序第16行中就在释放后仍然写这块内存。
下面的一段程序,就包括了内存动态管理中常见的错误。
#include <stdlib.h> #include <stdio.h> int main(int argc,char *argv[]) {
char *p=(char*)malloc(10); char *pt=p; int i; for(i=0;i<10;i++) {
p[i]=’z’;
} delete p; p[1]=’a’; free(pt); return 0; } Valgrind提示如下 ==25811== Mismatched free() / delete / delete [] ==25811== at 0x4A05130: operator delete(void*) (vg_replace_malloc.c:244) ==25811== by 0x400654: main (sample4.c:9) ==25811== Address 0x4C2F030 is 0 bytes inside a block of size 10 alloc\'d ==25811== at 0x4A05809: malloc (vg_replace_malloc.c:149) ==25811== by 0x400620: main (sample4.c:4) ==25811== ==25811== Invalid write of size 1 ==25811== at 0x40065D: main (sample4.c:10) ==25811== Address 0x4C2F031 is 1 bytes inside a block of size 10 free\'d ==25811== at 0x4A05130: operator delete(void*) (vg_replace_malloc.c:244) ==25811== by 0x400654: main (sample4.c:9) ==25811== ==25811== Invalid free() / delete / delete[] ==25811== at 0x4A0541E: free (vg_replace_malloc.c:233) ==25811== by 0x400668: main (sample4.c:11) ==25811== Address 0x4C2F030 is 0 bytes inside a block of size 10 free\'d ==25811== at 0x4A05130: operator delete(void*) (vg_replace_malloc.c:244) ==25811== by 0x400654: main (sample4.c:9) ==25811== ==25811== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 5 from 1) ==25811== malloc/free: in use at exit: 0 bytes in 0 blocks. ==25811== malloc/free: 1 allocs, 2 frees, 10 bytes allocated. ==25811== For counts of detected errors, rerun with: -v ==25811== All heap blocks were freed -- no leaks are possible.
非法写/读
int main() { int i, *x; x = (int *)malloc(10*sizeof(int)); for (i=0; i<11; i++) x[i] = i; free(x); } Valgrind提示如下 ==21483== Invalid write of size 4 ==21483== at 0x4004EA: main (sample6.c:6) ==21483== Address 0x4C2E058 is 0 bytes after a block of size 40 alloc\'d ==21483== at 0x4A05809: malloc (vg_replace_malloc.c:149) ==21483== by 0x4004C9: main (sample6.c:4) ==21483== ==21483== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1) ==21483== malloc/free: in use at exit: 0 bytes in 0 blocks. ==21483== malloc/free: 1 allocs, 1 frees, 40 bytes allocated. ==21483== For counts of detected errors, rerun with: -v ==21483== All heap blocks were freed -- no leaks are possible.
无效指针
#include <stdlib.h> int main() { char *x = malloc(10); x[10] = \'a\'; free(x); return 0; } Valgrind提示如下 ==15262== Invalid write of size 1 ==15262== at 0x4004D6: main (sample7.c:5) ==15262== Address 0x4C2E03A is 0 bytes after a block of size 10 alloc\'d ==15262== at 0x4A05809: malloc (vg_replace_malloc.c:149) ==15262== by 0x4004C9: main (sample7.c:4) ==15262== ==15262== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1) ==15262== malloc/free: in use at exit: 0 bytes in 0 blocks. ==15262== malloc/free: 1 allocs, 1 frees, 10 bytes allocated. ==15262== For counts of detected errors, rerun with: -v ==15262== All heap blocks were freed -- no leaks are possible.
重复释放
#include <stdlib.h> int main() { char *x = malloc(10); free(x); free(x); return 0; } Valgrind提示如下 ==15005== Invalid free() / delete / delete[] ==15005== at 0x4A0541E: free (vg_replace_malloc.c:233) ==15005== by 0x4004DF: main (sample8.c:6) ==15005== Address 0x4C2E030 is 0 bytes inside a block of size 10 free\'d ==15005== at 0x4A0541E: free (vg_replace_malloc.c:233) ==15005== by 0x4004D6: main (sample8.c:5) ==15005== ==15005== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 1) ==15005== malloc/free: in use at exit: 0 bytes in 0 blocks. ==15005== malloc/free: 1 allocs, 2 frees, 10 bytes allocated. ==15005== For counts of detected errors, rerun with: -v ==15005== All heap blocks were freed -- no leaks are possible.
综合
我们下面的例子中包括常见的几类内存问题:堆中的内存越界、踩内存、栈中的内存越界、非法指针。
#include <stdlib.h> #include <stdio.h> int main(void) { char *ptr = malloc(10); ptr[12] = \'a\'; // 内存越界 memcpy(ptr +1, ptr, 5); // 踩内存 char a[10]; a[12] = \'i\'; // 数组越界 free(ptr); // 重复释放 free(ptr); char *p1; *p1 = \'1\'; // 非法指针 return 0; } 编译: gcc -o invalidptr invalidptr.c -g 执行:valgrind --leak-check=full ./invalidptr 结果如下: [konten@tencent test_valgrind]$ valgrind --leak-check=full ./invalidptr ==29776== Memcheck, a memory error detector. ==29776== Copyright (C) 2002-2007, and GNU GPL\'d, by Julian Seward et al. ==29776== Using LibVEX rev 1732, a library for dynamic binary translation. ==29776== Copyright (C) 2004-2007, and GNU GPL\'d, by OpenWorks LLP. ==29776== Using valgrind-3.2.3, a dynamic binary instrumentation framework. ==29776== Copyright (C) 2000-2007, and GNU GPL\'d, by Julian Seward et al. ==29776== For more details, rerun with: -v ==29776== ==29776== Invalid write of size 1 //堆内存越界被查出来 ==29776== at 0x80483D2: main (invalidptr.c:7) ==29776== Address 0x4159034 is 2 bytes after a block of size 10 alloc\'d ==29776== at 0x401A846: 分享几个实用的代码片段(附代码例子)