调不尽的内存泄漏,用不完的Valgrind

Valgrind 安装

1. 到www.valgrind.org下载最新版valgrind-X.X.X.tar.bz2

2. 解压安装包:tar –jxvf valgrind-3.2.3.tar.bz2

3. 解压后生成目录valgrind-3.2.3

4. cd valgrind-3.2.3

5. 运行./autogen.sh设置环境(需要标准的autoconf工具)(可选)

6. ./configure;配置Valgrind,生成MakeFile文件,具体参数信息详见INSTALL文件。一般只需要设置--prefix=/where/you/want/it/installed

7. Make;编译Valgrind

8. make install;安装Valgrind

Valgrind包含的工具

Valgrind支持很多工具:memcheck,addrcheck,cachegrind,Massif,helgrind和Callgrind等。在运行Valgrind时,你必须指明想用的工具,如果省略工具名,默认运行memcheck。

1、memcheck

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

1)使用未初始化的内存

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

3)读/写内存越界

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

5)内存泄漏

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

7)src和dst的重叠

2、cachegrind

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

3、helgrind

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

它主要用来检查多线程程序中出现的竞争问题。Helgrind 寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为”Eraser” 的竞争检测算法,并做了进一步改进,减少了报告错误的次数。

4、Callgrind

Callgrind收集程序运行时的一些数据,函数调用关系等信息,还可以有选择地进行cache 模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

一般用法:

$valgrind --tool=callgrind ./sec_infod

会在当前目录下生成callgrind.out.[pid], 如果我们想结束程序, 可以

$killall callgrind

然后我们可以用

$callgrind_annotate --auto=yes callgrind.out.[pid] > log

$vi log

5、Massif

堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

6、lackey

lackey是一个示例程序,以其为模版可以创建你自己的工具。在程序结束后,它打印出一些基本的关于程序执行统计数据。

Valgrind的参数

用法: valgrind [options] prog-and-args [options]: 常用选项,适用于所有Valgrind工具

--tool=<name>

最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。

-h --help

显示所有选项的帮助,包括内核和选定的工具两者。

--version

显示valgrind内核的版本,每个工具都有各自的版本。

-q --quiet

安静地运行,只打印错误信息。

--verbose

更详细的信息。

--trace-children=<yes|no>

跟踪子线程? [default: no]

--track-fds=<yes|no>

跟踪打开的文件描述?[default: no]

--time-stamp=<yes|no>

增加时间戳到LOG信息? [default: no]

--log-fd=<number>

输出LOG到描述符文件 [2=stderr]

--log-file=<file>

将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID

--log-file-exactly=<file>

输出LOG信息到 file

LOG信息输出

--xml=yes

将信息以xml格式输出,只有memcheck可用

--num-callers=<number>

show <number> callers in stack traces [12]

--error-exitcode=<number>

如果发现错误则返回错误代码 [0=disable]

--db-attach=<yes|no>

当出现错误,valgrind会自动启动调试器gdb。[default: no]

--db-command=<command>

启动调试器的命令行选项[gdb -nw %f %p]

适用于Memcheck工具的相关选项:

--leak-check=<no|summary|full>

要求对leak给出详细信息? Leak是指,存在一块没有被引用的内存空间,或没有被释放的内存空间,如summary,只反馈一些总结信息,告诉你有多少个malloc,多少个free 等;如果是full将输出所有的leaks,也就是定位到某一个malloc/free。 [default: summary]

--show-reachable=<yes|no>

如果为no,只输出没有引用的内存leaks,或指向malloc返回的内存块中部某处的leaks [default: no]

更详细的参数指令见附录A。

Valgrind的使用

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

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

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

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

下面是用memcheck检查sample.c的例子

这里用到的示例程序文件名为:sample.c(如下所示),选用的编译器为gcc。

生成可执行程序

gcc –g sample.c –o sample

图1

运行Valgrind

valgrind --tool=memcheck ./sample

以下是运行上述命令后的输出

图2

左边显示类似行号的数字(10297)表示的是 Process ID。

最上面的红色方框表示的是 valgrind 的版本信息。

中间的红色方框表示 valgrind 通过运行被测试程序,发现的内存问题。通过阅读这些信息,可以发现:

l 这是一个对内存的非法写操作,非法写操作的内存是4 bytes。

l 发生错误时的函数堆栈,以及具体的源代码行号。

l 非法写操作的具体地址空间。

最下面的红色方框是对发现的内存问题和内存泄漏问题的总结。内存泄漏的大小(40 bytes)也能够被检测出来。

Valgrind的示例

1.使用未初始化的内存

代码如下

#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.

  

2.内存读写越界

代码如下

#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.

  

3srcdst内存覆盖

代码如下

#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.

4.动态内存管理错误

常见的内存分配方式分三种:静态存储,栈上分配,堆上分配。全局变量属于静态存储,它们是在编译时就被分配了存储空间,函数内的局部变量属于栈上分配,而最灵活的内存使用方式当属堆上分配,也叫做内存动态分配了。常用的内存动态分配函数包括:malloc, alloc, realloc, new等,动态释放函数包括free, delete。

一旦成功申请了动态内存,我们就需要自己对其进行内存管理,而这又是最容易犯错误的。常见的内存动态管理错误包括:

l 申请和释放不一致

由于 C++ 兼容 C,而 C 与 C++ 的内存申请和释放函数是不同的,因此在 C++ 程序中,就有两套动态内存管理函数。一条不变的规则就是采用 C 方式申请的内存就用 C 方式释放;用 C++ 方式申请的内存,用 C++ 方式释放。也就是用 malloc/alloc/realloc 方式申请的内存,用 free 释放;用 new 方式申请的内存用 delete 释放。在上述程序中,用 malloc 方式申请了内存却用 delete 来释放,虽然这在很多情况下不会有问题,但这绝对是潜在的问题。

l 申请和释放不匹配

申请了多少内存,在使用完成后就要释放多少。如果没有释放,或者少释放了就是内存泄露;多释放了也会产生问题。上述程序中,指针p和pt指向的是同一块内存,却被先后释放两次。

l 释放后仍然读写

本质上说,系统会在堆上维护一个动态内存链表,如果被释放,就意味着该块内存可以继续被分配给其他部分,如果内存被释放后再访问,就可能覆盖其他部分的信息,这是一种严重的错误,上述程序第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.

5.内存泄漏

代码如下

#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.

6.非法写/

代码如下

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.

7.无效指针

代码如下

#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.

8.重复释放

代码如下

#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.

Valgrind的局限

l Valgrind不对静态数组(分配在栈上)进行边界检查。如果在程序中声明了一个数组:

int main()

{

char x[10];

x[11] = 'a';

}

Valgrind则不会警告你,你可以把数组改为动态在堆上分配的数组,这样就可能进行边界检查了。这个方法好像有点得不偿失的感觉。

l Valgrind占用了更多的内存--可达两倍于你程序的正常使用量。如果你用Valgrind来检测使用大量内存的程序就会遇到问题,它可能会用很长的时间来运行测试。大多数情况下,这都不是问题,即使速度慢也仅是检测时速度慢,如果你用Valgrind来检测一个正常运行时速度就很慢的程序,这下问题就大了。 Valgrind不可能检测出你在程序中犯下的所有错误--如果你不检查缓冲区溢出,Valgrind也不会告诉你代码写了它不应该写的内存。

附录A:参数指令

基本选项:

这些选项对所有工具都有效。

-h --help

显示所有选项的帮助,包括内核和选定的工具两者。

--help-debug

和--help相同,并且还能显示通常只有Valgrind的开发人员使用的调试选项。

--version

显示Valgrind内核的版本号。工具可以有他们自已的版本号。这是一种保证工具只在它们可以运行的内核上工作的一种设置。这样可以减少在工具和内核之间版本兼容性导致奇怪问题的概率。

-q --quiet

安静的运行,只打印错误信息。在进行回归测试或者有其它的自动化测试机制时会非常有用。

-v --verbose

显示详细信息。在各个方面显示你的程序的额外信息,例如:共享对象加载,使用的重置,执行引擎和工具的进程,异常行为的警告信息。重复这个标记可以增加详细的级别。

-d

调试Valgrind自身发出的信息。通常只有Valgrind开发人员对此感兴趣。重复这个标记可以产生更详细的输出。如果你希望发送一个bug报告,通过-v -v -d -d生成的输出会使你的报告更加有效。

--tool=<toolname>
[default: memcheck]

运行toolname指定的Valgrind,例如,Memcheck, Addrcheck, Cachegrind,等等。

--trace-children=<yes|no>
[default: no]

当这个选项打开时,Valgrind会跟踪到子进程中。这经常会导致困惑,而且通常不是你所期望的,所以默认这个选项是关闭的。

--track-fds=<yes|no>
[default: no]

当这个选项打开时,Valgrind会在退出时打印一个打开文件描述符的列表。每个文件描述符都会打印出一个文件是在哪里打开的栈回溯,和任何与此文件描述符相关的详细信息比如文件名或socket信息。

--time-stamp=<yes|no>
[default: no]

当这个选项打开时,每条信息之前都有一个从程序开始消逝的时间,用天,小时,分钟,秒和毫秒表示。

--log-fd=<number>
[default: 2, stderr]

指定Valgrind把它所有的消息都输出到一个指定的文件描述符中去。默认值2, 是标准错误输出(stderr)。注意这可能会干扰到客户端自身对stderr的使用, Valgrind的输出与客户程序的输出将穿插在一起输出到stderr。

--log-file=<filename>

指定Valgrind把它所有的信息输出到指定的文件中。实际上,被创建文件的文件名是由filename、'.'和进程号连接起来的(即<filename>.<pid>),从而每个进程创建不同的文件。

--log-file-exactly=<filename>

类似于--log-file,但是后缀".pid"不会被添加。如果设置了这个选项,使用Valgrind跟踪多个进程,可能会得到一个乱七八糟的文件。

--log-file-qualifier=<VAR>

当和--log-file一起使用时,日志文件名将通过环境变量$VAR来筛选。这对于MPI程序是有益的。更多的细节,查看手册2.3节 "注解"。

--log-socket=<ip-address:port-number>

指定Valgrind输出所有的消息到指定的IP,指定的端口。当使用1500端口时,端口有可能被忽略。如果不能建立一个到指定端口的连接,Valgrind将输出写到标准错误(stderr)。这个选项经常和一个Valgrind监听程序一起使用。更多的细节,查看手册2.3节 "注解"。

错误相关选项:

这些选项适用于所有产生错误的工具,比如Memcheck, 但是Cachegrind不行。

--xml=<yes|no>
[default: no]

当这个选项打开时,输出将是XML格式。这是为了使用Valgrind的输出做为输入的工具,例如GUI前端更加容易些。目前这个选项只在Memcheck时生效。

--xml-user-comment=<string>

在XML开头 附加用户注释,仅在指定了--xml=yes时生效,否则忽略。

--demangle=<yes|no>
[default: yes]

打开/关闭C++的名字自动解码。默认打开。当打开时,Valgrind将尝试着把编码过的C++名字自动转回初始状态。这个解码器可以处理g++版本为2.X,3.X或4.X生成的符号。一个关于名字编码解码重要的事实是,禁止文件中的解码函数名仍然使用他们未解码的形式。Valgrind在搜寻可用的禁止条目时不对函数名解码,因为这将使禁止文件内容依赖于Valgrind的名字解码机制状态,会使速度变慢,且无意义。

--num-callers=<number>
[default: 12]

默认情况下,Valgrind显示12层函数调用的函数名有助于确定程序的位置。可以通过这个选项来改变这个数字。这样有助在嵌套调用的层次很深时确定程序的位置。注意错误信息通常只回溯到最顶上的4个函数。(当前函数,和它的3个调用者的位置)。所以这并不影响报告的错误总数。这个值的最大值是50。注意高的设置会使Valgrind运行得慢,并且使用更多的内存,但是在嵌套调用层次比较高的程序中非常实用。

--error-limit=<yes|no>
[default: yes]

当这个选项打开时,在总量达到10,000,000,或者1,000个不同的错误,Valgrind停止报告错误。这是为了避免错误跟踪机制在错误很多的程序下变成一个巨大的性能负担。

--error-exitcode=<number>
[default: 0]

指定如果Valgrind在运行过程中报告任何错误时的退出返回值,有两种情况;当设置为默认值(零)时,Valgrind返回的值将是它模拟运行的程序的返回值。当设置为非零值时,如果Valgrind发现任何错误时则返回这个值。在Valgrind做为一个测试工具套件的部分使用时这将非常有用,因为使测试工具套件只检查Valgrind返回值就可以知道哪些测试用例Valgrind报告了错误。

--show-below-main=<yes|no>
[default: no]

默认地,错误时的栈回溯不显示main()之下的任何函数(或者类似的函数像glibc的__libc_start_main(),如果main()没有出现在栈回溯中);这些大部分都是令人厌倦的C库函数。如果打开这个选项,在main()之下的函数也将会显示。

--suppressions=<filename>
[default: $PREFIX/lib/valgrind/default.supp]

指定一个额外的文件读取不需要理会的错误;你可以根据需要使用任意多的额外文件。

--gen-suppressions=<yes|no|all>
[default: no]

当设置为yes时,Valgrind将会在每个错误显示之后自动暂停并且打印下面这一行:---- Print suppression ? --- [Return/N/n/Y/y/C/c] ----这个提示的行为和--db-attach选项(见下面)相同。如果选择是,Valgrind会打印出一个错误的禁止条目,你可以把它剪切然后粘帖到一个文件,如果不希望在将来再看到这个错误信息。当设置为all时,Valgrind会对每一个错误打印一条禁止条目,而不向用户询问。这个选项对C++程序非常有用,它打印出编译器调整过的名字。注意打印出来的禁止条目是尽可能的特定的。如果需要把类似的条目归纳起来,比如在函数名中添加通配符。并且,有些时候两个不同的错误也会产生同样的禁止条目,这时Valgrind就会输出禁止条目不止一次,但是在禁止条目的文件中只需要一份拷贝(但是如果多于一份也不会引起什么问题)。并且,禁止条目的名字像<在这儿输入一个禁止条目的名字>;名字并不是很重要,它只是和-v选项一起使用打印出所有使用的禁止条目记录。

--db-attach=<yes|no>
[default: no]

当这个选项打开时,Valgrind将会在每次打印错误时暂停并打出如下一行:---- Attach to
debugger ? --- [Return/N/n/Y/y/C/c] ---- 按下回车,或者N、回车,n、回车,Valgrind不会对这个错误启动调试器。按下Y、回车,或者y、回车,Valgrind会启动调试器并设定在程序运行的这个点。当调试结束时,退出,程序会继续运行。在调试器内部尝试继续运行程序,将不会生效。按下C、回车,或者c、回车,Valgrind不会启动一个调试器,并且不会再次询问。注意:--db-attach=yes与--trace-children=yes有冲突。你不能同时使用它们。Valgrind在这种情况下不能启动。

2002.05: 这是一个历史的遗留物,如果这个问题影响到你,请发送邮件并投诉这个问题。

2002.11:如果你发送输出到日志文件或者到网络端口,我猜这不会让你有任何感觉。不须理会。

--db-command=<command>
[default: gdb -nw %f %p]

通过--db-attach指定如何使用调试器。默认的调试器是gdb.默认的选项是一个运行时扩展Valgrind的模板。 %f会用可执行文件的文件名替换,%p会被可执行文件的进程ID替换。

这指定了Valgrind将怎样调用调试器。默认选项不会因为在构造时是否检测到了GDB而改变,通常是/usr/bin/gdb.使用这个命令,你可以指定一些调用其它的调试器来替换。

给出的这个命令字串可以包括一个或多个%p %f扩展。每一个%p实例都被解释成将调试的进程的PID,每一个%f实例都被解释成要调试的进程的可执行文件路径。

--input-fd=<number>
[default: 0, stdin]

使用--db-attach=yes和--gen-suppressions=yes选项,在发现错误时,Valgrind会停下来去读取键盘输入。默认地,从标准输入读取,所以关闭了标准输入的程序会有问题。这个选项允许你指定一个文件描述符来替代标准输入读取。

--max-stackframe=<number>
[default: 2000000]

栈的最大值。如果栈指针的偏移超过这个数量,Valgrind则会认为程序是切换到了另外一个栈执行。如果在程序中有大量的栈分配的数组,你可能需要使用这个选项。valgrind保持对程序栈指针的追踪。如果栈指针的偏移超过了这个数量,Valgrind假定你的程序切换到了另外一个栈,并且Memcheck行为与栈指

针的偏移没有超出这个数量将会不同。通常这种机制运转得很好。然而,如果你的程序在栈上申请了大的结构,这种机制将会表现得愚蠢,并且Memcheck将会报告大量的非法栈内存访问。这个选项允许把这个阀值设置为其它值。应该只在Valgrind的调试输出中显示需要这么做时才使用这个选项。在这种情况下,它会告诉你应该指定的新的阀值。普遍地,在栈中分配大块的内存是一个坏的主意。因为这很容易用光你的栈空间,尤其是在内存受限的系统或者支持大量小堆栈的线程的系统上,因为Memcheck执行的错误检查,对于堆上的数据比对栈上的数据要高效很多。如果你使用这个选项,你可能希望考虑重写代码在堆上分配内存而不是在栈上分配。

MALLOC()相关的选项:

对于使用自有版本的malloc() (例如Memcheck和massif),下面的选项可以使用。

--alignment=<number>
[default: 8]

默认Valgrind的malloc(),realloc(), 等等,是8字节对齐地址的。这是大部分处理器的标准。然而,一些程序可能假定malloc()等总是返回16字节或更多对齐的内存。提供的数值必须在8和4096区间之内,并且必须是2的幂数。

非通用选项:

这些选项可以用于所有的工具,它们影响Valgrind core的几个特性。大部分人不会用到这些选项。

--run-libc-freeres=<yes|no>
[default: yes]

GNU C库(libc.so),所有程序共用的,可能会分配一部分内存自已用。通常在程序退出时释放内存并不麻烦 -- 这里没什么问题,因为Linux内核在一个进程退出时会回收进程全部的资源,所以这只是会造成速度慢。glibc的作者认识到这样会导致内存检查器,像Valgrind,在退出时检查内存错误的报告glibc的内存泄漏问题,为了避免这个问题,他们提供了一个__libc_freeres()例程特别用来让glibc释放分配的所有内存。因此Memcheck在退出时尝试着去运行__libc_freeres()。不幸的是,在glibc的一些版本中,__libc_freeres是有bug会导致段错误的。这在Red Hat 7.1上有特别声明。所以,提供这个选项来决定是否运行__libc_freeres。如果你的程序看起来在Valgrind上运行得很好,但是在退出时发生段错误,你可能需要指定--run-libc-freeres=no来修正,这将可能错误的报告libc.so的内存泄漏。

--sim-hints=hint1,hint2,...

传递杂凑的提示给Valgrind,轻微的修改模拟行为的非标准或危险方式,可能有助于模拟奇怪的特性。默认没有提示打开。小心使用!目前已知的提示有:

l lax-ioctls: 对ioctl的处理非常不严格,唯一的假定是大小是正确的。不需要在写时缓冲区完全的初始化。没有这个,用大量的奇怪的ioctl命令来使用一些设备驱动将会非常烦人。

l enable-inner:打开某些特殊的效果,当运行的程序是Valgrind自身时。

--kernel-variant=variant1,variant2,...

处理系统调用和ioctls在这个平台的默认核心上产生不同的变量。这有助于运行在改进过的内核或者支持非标准的ioctls上。小心使用。如果你不理解这个选项做的是什么那你几乎不需要它。已经知道的变量有:

l bproc: 支持X86平台上的sys_broc系统调用。这是为了运行在BProc,它是标准Linux的一个变种,有时用来构建集群。

--show-emwarns=<yes|no>
[default: no]

当这个选项打开时,Valgrind在一些特定的情况下将对CPU仿真产生警告。通常这些都是不引人注意的。

--smc-check=<none|stack|all>
[default: stack]

这个选项控制Valgrind对自我修改的代码的检测。Valgrind可以不做检测,可以检测栈中自我修改的代码,或者任意地方检测自我修改的代码。注意默认选项是捕捉绝大多数情况,到目前我们了解的情况为止。使用all选项时会极大的降低速度。(但是用none选项运行极少影响速度,因为对大多数程序,非常少的代码被添加到栈中)

调试VALGRIND选项:

还有一些选项是用来调试Valgrind自身的。在运行一般的东西时不应该需要的。如果你希望看到选项列表,使用--help-debug选项。

内存检查选项:

--leak-check=<no|summary|yes|full>
[default: summary]

当这个选项打开时,当客户程序结束时查找内存泄漏。内存泄漏意味着有用malloc分配内存块,但是没有用free释放,而且没有指针指向这块内存。这样的内存块永远不能被程序释放,因为没有指针指向它们。如果设置为summary,Valgrind会报告有多少内存泄漏发生了。如果设置为full或yes,Valgrind给出每一个独立的泄漏的详细信息。

--show-reachable=<yes|no>
[default: no]

当这个选项关闭时,内存泄漏检测器只显示没有指针指向的内存块,或者只能找到指向块中间的指针。当这个选项打开时,内存泄漏检测器还报告有指针指向的内存块。这些块是最有可能出现内存泄漏的地方。你的程序可能,至少在原则上,应该在退出前释放这些内存块。这些有指针指向的内存块和没有指针指向的内存块,或者只有内部指针指向的块,都可能产生内存泄漏,因为实际上没有一个指向块起始的指针可以拿来释放,即使你想去释放它。

--leak-resolution=<low|med|high>
[default: low]

在做内存泄漏检查时,确定memcheck将怎么样考虑不同的栈是相同的情况。当设置为low时,只需要前两层栈匹配就认为是相同的情况;当设置为med,必须要四层栈匹配,当设置为high时,所有层次的栈都必须匹配。对于hardcore内存泄漏检查,你很可能需要使用--leak-resolution=high和--num-callers=40或者更大的数字。注意这将产生巨量的信息,这就是为什么默认选项是四个调用者匹配和低分辨率的匹配。注意--leak-resolution= 设置并不影响memcheck查找内存泄漏的能力。它只是改变了结果如何输出。

--freelist-vol=<number>
[default: 5000000]

当客户程序使用free(C中)或者delete(C++)释放内存时,这些内存并不是马上就可以用来再分配的。这些内存将被标记为不可访问的,并被放到一个已释放内存的队列中。这样做的目的是,使释放的内存再次被利用的点尽可能的晚。这有利于memcheck在内存块释放后这段重要的时间检查对块不合法的访问。这个选项指定了队列所能容纳的内存总容量,以字节为单位。默认的值是5000000字节。增大这个数目会增加memcheck使用的内存,但同时也增加了对已释放内存的非法使用的检测概率。

--workaround-gcc296-bugs=<yes|no>
[default: no]

当这个选项打开时,假定读写栈指针以下的一小段距离是gcc 2.96的bug,并且不报告为错误。距离默认为256字节。注意gcc 2.96是一些比较老的Linux发行版(RedHat 7.X)的默认编译器,所以你可能需要使用这个选项。如果不是必要请不要使用这个选项,它可能会使一些真正的错误溜掉。一个更好的解决办法是使用较新的,修正了这个bug的gcc/g++版本。

--partial-loads-ok=<yes|no>
[default: no]

控制memcheck如何处理从地址读取时字长度,字对齐,因此哪些字节是可以寻址的,哪些是不可以寻址的。当设置为yes是,这样的读取并不抛出一个寻址错误。而是从非法地址读取的V字节显示为未定义,访问合法地址仍然是像平常一样映射到内存。设置为no时,从部分错误的地址读取与从完全错误的地址读取同样处理:抛出一个非法地址错误,结果的V字节显示为合法数据。注意这种代码行为是违背ISO C/C++标准,应该被认为是有问题的。如果可能,这种代码应该修正。这个选项应该只是做为一个最后考虑的方法。

--undef-value-errors=<yes|no>
[default: yes]

控制memcheck是否检查未定义值的危险使用。当设为yes时,Memcheck的行为像Addrcheck, 一个轻量级的内存检查工具,是Valgrind的一个部分,它并不检查未定义值的错误。使用这个选项,如果你不希望看到未定义值错误。

CACHEGRIND选项:

手动指定I1/D1/L2缓冲配置,大小是用字节表示的。这三个必须用逗号隔开,中间没有空格,例如: valgrind --tool=cachegrind --I1=65535,2,64你可以指定一个,两个或三个I1/D1/L2缓冲。如果没有手动指定,每个级别使用普通方式(通过CPUID指令得到缓冲配置,如果失败,使用默认值)得到的配置。

--I1=<size>,<associativity>,<line
size>

指定第一级指令缓冲的大小,关联度和行大小。

--D1=<size>,<associativity>,<line
size>

指定第一级数据缓冲的大小,关联度和行大小。

--L2=<size>,<associativity>,<line
size>

指定第二级缓冲的大小,关联度和行大小。

CALLGRIND选项:

--heap=<yes|no>
[default: yes]

当这个选项打开时,详细的追踪堆的使用情况。关闭这个选项时,massif.pid.txt或massif.pid.html将会非常的简短。

--heap-admin=<number>
[default: 8]

每个块使用的管理字节数。这只能使用一个平均的估计值,因为它可能变化。glibc使用的分配器每块需要4~15字节,依赖于各方面的因素。管理已经释放的块也需要空间,尽管massif不计算这些。

--stacks=<yes|no>
[default: yes]

当打开时,在剖析信息中包含栈信息。多线程的程序可能有多个栈。

--depth=<number>
[default: 3]

详细的堆信息中调用过程的深度。增加这个值可以给出更多的信息,但是massif会更使这个程序运行得慢,使用更多的内存,并且产生一个大的massif.pid.txt或者massif.pid.hp文件。

--alloc-fn=<name>

指定一个分配内存的函数。这对于使用malloc()的包装函数是有用的,可以用它来填充原来无效的上下文信息。(这些函数会给出无用的上下文信息,并在图中给出无意义的区域)。指定的函数在上下文中被忽略,例如,像对malloc()一样处理。这个选项可以在命令行中重复多次,指定多个函数。

--format=<text|html>
[default: text]

产生text或者HTML格式的详细堆信息,文件的后缀名使用.txt或者.html。

HELGRIND选项:

--private-stacks=<yes|no>
[default: no]

假定线程栈是私有的。

--show-last-access=<yes|some|no>
[default: no]

显示最后一次字访问出错的位置。

LACKEY选项:

--fnname=<name>
[default: _dl_runtime_resolve()]

对<name>函数计数。

--detailed-counts=<no|yes>
[default: no]

对读取,存储和alu操作计数。

4.3. Explanation of error messages from Memcheck

Despite considerable sophistication under the hood, Memcheck can only really detect two kinds of errors: use of illegal addresses, and use of undefined values. Nevertheless, this is enough to help you discover all sorts of memory-management problems in your code.

[Valgrind Memcheck仅能检测两种错误,一是非法地址使用,二是未定义值使用。但这足以帮助你检查出代码中各种类型的内存管理问题。]

This section presents a quick summary of what error messages mean. The precise behaviour of the error-checking machinery is described in Details of Memcheck's checking machinery.

4.3.1. Illegal read / Illegal write errors [非法读写错误]

For example:

Invalid read of size 4
at 0x40F6BBCC: (within /usr/lib/libpng.so.2.1.0.9)
by 0x40F6B804: (within /usr/lib/libpng.so.2.1.0.9)
by 0x40B07FF4: read_png_image(QImageIO *) (kernel/qpngio.cpp:326)
by 0x40AC751B: QImageIO::read() (kernel/qimage.cpp:3621)
Address 0xBFFFF0E0 is not stack'd, malloc'd or free'd

This happens when your program reads or writes memory at a place which Memcheck reckons it shouldn't.

[上面的错误信息表示Memcheck认为你的程序错误的进行了对内存某个位置的读或写操作。]

In this example, the program did a 4-byte read at address 0xBFFFF0E0, somewhere within the system-supplied library libpng.so.2.1.0.9, which was called from somewhere else in the same library, called from line 326 of qpngio.cpp, and so on.

[具体信息表明,程序在 0xBFFFF0E0 处进行了4个字节的读操作,该操作发生在系统提供的 libpng.so.2.1.0.9 中,进而显示该库的调用由 qpngio.cpp 文件的 326 行处引起,等等类似描述。]

Memcheck tries to establish what the illegal address might relate to, since that's often useful. So, if it points into a block of memory which has already been freed, you'll be informed of this, and also where the block was free'd at. Likewise, if it should turn out to be just off the end of a malloc'd block, a common result of off-by-one-errors in array subscripting, you'll be informed of this fact, and also where the block was malloc'd.

[Memcheck尽可能地帮助用户指出错误地址与什么有关联,以提供有用信息。例如,如果程序使用了已经被释放的内存块,那么你除了能从Memcheck得知这一情况外,还能得到具体是哪一块内存被释放了。再比如,对于数组处理中通常会发生的越界现象,Memcheck 不仅会通知用户这一情况的出现,还会指出出错位置。]

In this example, Memcheck can't identify the address. Actually the address is on the stack, but, for some reason, this is not a valid stack address -- it is below the stack pointer and that isn't allowed. In this particular case it's probably caused by gcc generating invalid code, a known bug in some ancient versions of gcc.

[在上面的例子中, Memcheck不能确定具体地址。实际上,地址位于栈中,但是由于某些原因,它是一个无效的栈地址。]

Note that Memcheck only tells you that your program is about to access memory at an illegal address. It can't stop the access from happening. So, if your program makes an access which normally would result in a segmentation fault, you program will still suffer the same fate -- but you will get a message from Memcheck immediately prior to this. In this particular example, reading junk on the stack is non-fatal, and the program stays alive.

[注意:Memcheck 只能告诉你你的程序将访问非法地址内存,但它不能阻止访问。所以,如果你的程序访问中导致段错误,那程序将一直带有这个错误,但 Memcheck 会在此发生之前给你提示信息。在上面的例子中,对栈上的垃圾区域进行读操作没有风险,程序也不会崩溃。]

4.3.2. Use of uninitialised values [值在初始化前使用]

For example:

Conditional jump or move depends on uninitialised value(s)
at 0x402DFA94: _IO_vfprintf (_itoa.h:49)
by 0x402E8476: _IO_printf (printf.c:36)
by 0x8048472: main (tests/manuel1.c:8)

An uninitialised-value use error is reported when your program uses a value which hasn't been initialised -- in other words, is undefined. Here, the undefined value is used somewhere inside the printf() machinery of the C library. This error was reported when running the following small program:

[未初始值的使用错误,即未定义先使用。如下例:]

int main()
{
int x;
printf ("x = %d\n", x);
}

It is important to understand that your program can copy around junk (uninitialised) data as much as it likes.

[你的程序能够对垃圾(未初始)数据进行拷贝,理解这点很重要。]

Memcheck observes this and keeps track of the data, but does not complain. A complaint is issued only when your program attempts to make use of uninitialised data. In this example, x is uninitialised. Memcheck observes the value being passed to _IO_printf and thence to _IO_vfprintf, but makes no comment. However, _IO_vfprintf has to examine the value of x so it can turn it into the corresponding ASCII string, and it is at this point that Memcheck complains.

[Memcheck 观察并跟踪数据,但它暂时不会报错。只有当你试图使用未初始的数据时才会报错。在上面的例子中,x 未被初始化,Memcheck 能够观察到该值传给 _IO_printf,然后传给 _IO_vfprintf,但此时不报错。而当 _IO_vfprintf 必须要检测 x 的值时,它需要将 x 值转换成相应的 ASCII 码字符串,此时 Memcheck就要报错了。]

Sources of uninitialised data tend to be:

[未初始值的来源包括:]

  • Local variables in procedures which have not been initialised, as in the example above.

  • [本地变量在执行过程中未初始化,如上例。]
  • The contents of malloc'd blocks, before you write something there. In C++, the new operator is a wrapper round malloc, so if you create an object with new, its fields will be uninitialised until you (or the constructor) fill them in.

  • [malloc分配的块内容未能初始化,特别是在写入数据之前。在C++中,new 操作符包含 malloc 操作,所以,如果你用 new 创建了一个对象,你必须为其指向的内存填充内容,或使用构造函数初始后,才算真正初始化。]

4.3.3. Illegal frees [非法释放内存]

For example:

Invalid free()
at 0x4004FFDF: free (vg_clientmalloc.c:577)
by 0x80484C7: main (tests/doublefree.c:10)
Address 0x3807F7B4 is 0 bytes inside a block of size 177 free'd
at 0x4004FFDF: free (vg_clientmalloc.c:577)
by 0x80484C7: main (tests/doublefree.c:10)

Memcheck keeps track of the blocks allocated by your program with malloc/new, so it can know exactly whether or not the argument to free/delete is legitimate or not. Here, this test program has freed the same block twice. As with the illegal read/write errors, Memcheck attempts to make sense of the address free'd. If, as here, the address is one which has previously been freed, you wil be told that -- making duplicate frees of the same block easy to spot.

[因为Memcheck 能够保持对 malloc/new 分配的块的跟踪,所以,它能够确切地知道 free/delete 是否合法。这里,可以看到上面测试程序对同一块释放了两次。与处理非法读写错误一样,Memcheck 试图使 free'd 出错地址看起来更有意义。见实例]

4.3.4. When a block is freed with an inappropriate deallocation function [使用不恰当的释放函数释放块]

In the following example, a block allocated with new[] has wrongly been deallocated with free:

[下面的例子显示了使用free() 错误地释放了 new[] 分配的内存:]

Mismatched free() / delete / delete []
at 0x40043249: free (vg_clientfuncs.c:171)
by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149)
by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60)
by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44)
Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd
at 0x4004318C: operator new[](unsigned int) (vg_clientfuncs.c:152)
by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314)
by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416)
by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272)

In C++ it's important to deallocate memory in a way compatible with how it was allocated. The deal is:

[C++中,以正确的方式释放内存非常重要,具体如下:]

  • If allocated with malloccallocreallocvalloc or memalign, you must deallocate with free.

  • [如果内存使用 malloc\calloc\realloc\valloc\memalign 分配,你必须使用 free 释放。]
  • If allocated with new[], you must deallocate with delete[].

  • [如果使用 new[] 分配,你必须使用 delete[] 释放。]
  • If allocated with new, you must deallocate with delete.

  • [如果使用 new 分配,你必须使用 delete 释放。]

The worst thing is that on Linux apparently it doesn't matter if you do mix these up, but the same program may then crash on a different platform, Solaris for example. So it's best to fix it properly. According to the KDE folks "it's amazing how many C++ programmers don't know this".

[最糟糕的是,如果搞混,有时在 Linux 平台不会出错,但在其他平台,如 Solaris 却会使程序崩溃。所以,最好还是修改程序,恰当使用每种方式。也不怪 KDE 的开发者们在惊叹“怎么会有那么多程序员不知道这一点。”]

The reason behind the requirement is as follows. In some C++ implementations, delete[] must be used for objects allocated by new[] because the compiler stores the size of the array and the pointer-to-member to the destructor of the array's content just before the pointer actually returned. This implies a variable-sized overhead in what's returned by new or new[].

4.3.5. Passing system call parameters with inadequate read/write permissions [传递系统调用参数时读写权限不足]

Memcheck checks all parameters to system calls:

  • It checks all the direct parameters themselves.

  • [检查直接参数]
  • Also, if a system call needs to read from a buffer provided by your program, Memcheck checks that the entire buffer is addressable and has valid data, ie, it is readable.

  • [程序提供缓冲区参数]
  • Also, if the system call needs to write to a user-supplied buffer, Memcheck checks that the buffer is addressable.

  • [用户提供缓冲区参数]

After the system call, Memcheck updates its tracked information to precisely reflect any changes in memory permissions caused by the system call.

Here's an example of two system calls with invalid parameters:

#include <stdlib.h>
#include <unistd.h>
int main( void )
{
char* arr = malloc(10);
int* arr2 = malloc(sizeof(int));
write( 1 /* stdout */, arr, 10 );
exit(arr2[0]);
}

You get these complaints ...

Syscall param write(buf) points to uninitialised byte(s)
at 0x25A48723: __write_nocancel (in /lib/tls/libc-2.3.3.so)
by 0x259AFAD3: __libc_start_main (in /lib/tls/libc-2.3.3.so)
by 0x8048348: (within /auto/homes/njn25/grind/head4/a.out)
Address 0x25AB8028 is 0 bytes inside a block of size 10 alloc'd
at 0x259852B0: malloc (vg_replace_malloc.c:130)
by 0x80483F1: main (a.c:5) Syscall param exit(error_code) contains uninitialised byte(s)
at 0x25A21B44: __GI__exit (in /lib/tls/libc-2.3.3.so)
by 0x8048426: main (a.c:8)

... because the program has (a) tried to write uninitialised junk from the malloc'd block to the standard output, and (b) passed an uninitialised value to exit. Note that the first error refers to the memory pointed to by buf (not buf itself), but the second error refers directly to exit's argument arr2[0].

4.3.6. Overlapping source and destination blocks [源头和目标块重叠]

The following C library functions copy some data from one memory block to another (or something similar): memcpy()strcpy()strncpy()strcat()strncat(). The blocks pointed to by their src and dst pointers aren't allowed to overlap. Memcheck checks for this.

[C库中一些具有拷贝功能的函数,如memcpy()strcpy()strncpy()strcat()strncat(),他们将数据从一个内存块中拷贝至另一个中。源头和目标内存块不允许有重叠, Memcheck 可以检查这种错误。]

For example:

==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
==27492== at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
==27492== by 0x804865A: main (overlap.c:40)

You don't want the two blocks to overlap because one of them could get partially overwritten by the copying.

You might think that Memcheck is being overly pedantic reporting this in the case where dst is less than src. For example, the obvious way to implement memcpy() is by copying from the first byte to the last. However, the optimisation guides of some architectures recommend copying from the last byte down to the first. Also, some implementations of memcpy() zero dst before copying, because zeroing the destination's cache line(s) can improve performance.

In addition, for many of these functions, the POSIX standards have wording along the lines "If copying takes place between objects that overlap, the behavior is undefined." Hence overlapping copies violate the standard.

The moral of the story is: if you want to write truly portable code, don't make any assumptions about the language implementation.

4.3.7. Memory leak detection [内存漏洞检测]

Memcheck keeps track of all memory blocks issued in response to calls to malloc/calloc/realloc/new. So when the program exits, it knows which blocks have not been freed.

[Memcheck 保持对所有 malloc/calloc/realloc/new 分配的内存块的跟踪,因此当程序退出时,它能检测出哪一块未被释放。]

If --leak-check is set appropriately, for each remaining block, Memcheck scans the entire address space of the process, looking for pointers to the block. Each block fits into one of the three following categories.

[如果 --leak-check 被设置,则 Memcheck 将扫描整个空间地址,寻找指向块的指针。处理后,在 Memcheck 中可以将块分类三类:]

  • Still reachable: A pointer to the start of the block is found. This usually indicates programming sloppiness. Since the block is still pointed at, the programmer could, at least in principle, free it before program exit. Because these are very common and arguably not a problem, Memcheck won't report such blocks unless --show-reachable=yes is specified.

  • [仍可被访问的块:存在一个指针指向该块的起始位置。这通常表明编程水平太潮。因为程序退出后程序员至少要将内存释放掉,而不应再有指针指向它。Memcheck 通常不会报告此类问题,除非你使用了 --show-reachable=yes 选项,因为这些问题很常见且存有争议。]
  • Possibly lost, or "dubious": A pointer to the interior of the block is found. The pointer might originally have pointed to the start and have been moved along, or it might be entirely unrelated. Memcheck deems such a block as "dubious", because it's unclear whether or not a pointer to it still exists.

  • [可能丢失或“有疑问的块”:指向块内部的指针被发现。该指针很可能开始时指向块首,后来被移动,或者最终没有任何关联。 Memcheck 认为这样的块为“有疑问的块”,因为不清楚是否还有指针指向它。]
  • Definitely lost, or "leaked": The worst outcome is that no pointer to the block can be found. The block is classified as "leaked", because the programmer could not possibly have freed it at program exit, since no pointer to it exists. This is likely a symptom of having lost the pointer at some earlier point in the program.

  • [明显丢失或“内存泄漏”:最坏的结果是某些块已没有指针指向,却还能被发现。这样的块被归入“泄漏”类,是因为程序员在程序终止时没有释放,因为没有指针指向他们。这很象程序运行前期指针丢失的症状。]

For each block mentioned, Memcheck will also tell you where the block was allocated. It cannot tell you how or why the pointer to a leaked block has been lost; you have to work that out for yourself. In general, you should attempt to ensure your programs do not have any leaked or dubious blocks at exit.

[对以上每种情况,Memcheck 都会告诉你该块在何处被分配。但它不会告诉你如何和为什么指向该块的指针丢失。你必须自己进一步确定。总起来说,你应该首先确保程序在退出时不会有任何泄漏或有泄漏倾向的块存在。]

For example:

8 bytes in 1 blocks are definitely lost in loss record 1 of 14
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: mk (leak-tree.c:11)
by 0x........: main (leak-tree.c:39) 88 (8 direct, 80 indirect) bytes in 1 blocks are definitely lost
in loss record 13 of 14
at 0x........: malloc (vg_replace_malloc.c:...)
by 0x........: mk (leak-tree.c:11)
by 0x........: main (leak-tree.c:25)

The first message describes a simple case of a single 8 byte block that has been definitely lost. The second case mentions both "direct" and "indirect" leaks. The distinction is that a direct leak is a block which has no pointers to it. An indirect leak is a block which is only pointed to by other leaked blocks. Both kinds of leak are bad.

[第一部分描述了有8字节块明确丢失,第二部分提到了“直接”和“间接”泄漏,其区别是直接泄漏为存在一个内存块,而没有指针指向它。间接泄漏则为存在一个块,而存有其他泄漏块指向该块。两种情况都不是好事。]

The precise area of memory in which Memcheck searches for pointers is: all naturally-aligned machine-word-sized words found in memory that Memcheck's records indicate is both accessible and initialised.

[以上提到的 Memcheck用于寻找指针的内存块的确切区域指的是:内存中所有能过找到的自然关联的、按机器字长度计算的 words, 以上所指内存是指 Memcheck 记录中表明可被访问和可被初始化的内存区域。]

调不尽的内存泄漏,用不完的Valgrind的更多相关文章

  1. 如何排查Java内存泄漏?看完我给跪了!

    没有经验的程序员经常认为Java的自动垃圾回收完全使他们免于担心内存管理.这是一个常见的误解:虽然垃圾收集器做得很好,但即使是最好的程序员也完全有可能成为严重破坏内存泄漏的牺牲品.让我解释一下. 当不 ...

  2. JAVA内存泄漏解决办法

    JVM调优工具 Jconsole,jProfile,VisualVM Jconsole : jdk自带,功能简单,但是可以在系统有一定负荷的情况下使用.对垃圾回收算法有很详细的跟踪.详细说明参考这里 ...

  3. C/C++的内存泄漏检测工具Valgrind memcheck的使用经历

    Linux下的Valgrind真是利器啊(不知道Valgrind的请自觉查看参考文献(1)(2)),帮我找出了不少C++中的内存管理错误,前一阵子还在纠结为什么VS 2013下运行良好的程序到了Lin ...

  4. Valgrind 内存泄漏工具

    Valgrind 是一款 Linux下(支持 x86.x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和 ...

  5. Linux C/C++内存泄漏检测工具:Valgrind

    Valgrind 是一款 Linux下(支持 x86.x86_64和ppc32)程序的内存调试工具,它可以对编译后的二进制程序进行内存使用监测(C语言中的malloc和free,以及C++中的new和 ...

  6. C/C++中内存泄漏、内存溢出与野指针的解释与说明

    1.内存泄漏 内存泄漏是指我们在堆中申请(new/malloc)了一块内存,但是没有去手动的释放(delete/free)内存,导致指针已经消失,而指针指向的东西还在,已经不能控制这块内存, 所以就是 ...

  7. IOS性能调优系列:使用Instruments动态分析内存泄漏

    硬广:<IOS性能调优系列>第二篇,持续更新,欢迎关注. 第一篇介绍了Analyze对App做静态分析,可以发现应用中的内存泄漏问题,对于有些内存泄漏情况通过静态分析无法解决的,可以通过动 ...

  8. Day 18: 记filebeat内存泄漏问题分析及调优

    ELK 从发布5.0之后加入了beats套件之后,就改名叫做elastic stack了.beats是一组轻量级的软件,给我们提供了简便,快捷的方式来实时收集.丰富更多的数据用以支撑我们的分析.但由于 ...

  9. java内存泄漏的定位与分析

    1.为什么会发生内存泄漏 java 如何检测内在泄漏呢?我们需要一些工具进行检测,并发现内存泄漏问题,不然很容易发生down机问题. 编写java程序最为方便的地方就是我们不需要管理内存的分配和释放, ...

随机推荐

  1. ios 写项目的时候遇到的问题及解决方案(3)

    22.看了苹果的文档,里面有这一句话:All launch images must be PNG files and must reside in the top level of your appl ...

  2. c++之函数重载(函数匹配)

    Case void f(); void f(int); void f(int, int); void f(double, double = 3.14); 匹配原则: 1)其形参数量与本次调用提供的实参 ...

  3. How far away[HDU2586]

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  4. Spark作业调度

    Spark在任务提交时,主要存在于Driver和Executor的两个节点. (1)Driver的作用: 用于将所有要处理的RDD的操作转化为DAG,并且根据RDD DAG将JBO分割为多个Stage ...

  5. Oracle的回收站和闪回查询机制(二)

    上一篇中讲诉了Oracle中一些闪回查询(Flashback Query),这是利用回滚段信息来恢复一个或一些表到以前的一个时间点(一个快照).要注意的是,Flashback Query仅仅是查询以前 ...

  6. C++函数的传入参数是指针的指针(**)的详解

    要修改变量的值,需要使用变量类型的指针作为参数或者变量的引用.如果变量是一般类型的变量,例如int,则需要使用int 类型的指针类型int *作为参数或者int的引用类型int&.但是如果变量 ...

  7. RPI-Wireless-Hotspot

    http://elinux.org/RPI-Wireless-Hotspot What does it do? This project configures your Raspberry Pi to ...

  8. mysql 存储过程 计算报表

    把用例执行情况mysql表汇总起来 proc_write_report 汇总执行用例表中的测试数据 写入report 表,report表包括字段 report_id(自增)execution_flag ...

  9. Javascript进阶篇——(函数)笔记整理

    这节是根据慕课网和JavaScript DOM编程艺术一书加起来做的笔记 什么是函数如果需要多次使用同一段代码,可以把它们封装成一个函数.函数(function)就是一组允许在你的代码里随时调用的语句 ...

  10. windows下pycharm远程调试pyspark

    参考http://www.mamicode.com/info-detail-1523356.html1.远端执行:vi /etc/profile添加一行:PYTHONPATH=$SPARK_HOME/ ...