目录
要使用此工具,可以--tool=memcheck
在Valgrind命令行中指定。不过,由于Memcheck是默认工具,所以您不需要。
Memcheck是一个内存错误检测器。它可以检测C和C ++程序中常见的以下问题。
-
访问内存您不应该,例如超越和低估堆块,超出堆栈的顶部,以及在释放后访问内存。
-
使用未定义的值,即尚未初始化的值,或已从其他未定义值导出的值。
-
不正确的释放堆内存,比如双重释放堆块,或使用
malloc
/new
/new[]
对free
/delete
/delete[]
-
重叠
src
和dst
指针memcpy
以及相关功能。 -
将一个腥味(大概为负)值传递给
size
一个内存分配功能的参数。 -
内存泄漏。
像这样的问题可能难以通过其他方式找到,经常长时间不被发现,然后导致偶尔的,难以诊断的崩溃。
Memcheck发出一系列错误消息。本节将简要介绍哪些错误消息的意思。错误检查机械的精确行为在Memcheck检查机械的详细信息中有所描述。
例如:
大小4读取无效 在0x40F6BBCC :(在/usr/lib/libpng.so.2.1.0.9中) 由0x40F6B804 :(在/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) 地址0xBFFFF0E0不是stack'd,malloc'd或free'd
当您的程序在Memcheck认为不应该读取或写入内存时,会发生这种情况。在这个例子中,程序做了4个字节的地址处读0xBFFFF0E0,内系统提供的库libpng.so.2.1.0.9,将其从其他地方以相同的库调用某处,从326行调用qpngio.cpp
,等等上。
Memcheck尝试确定非法地址可能涉及什么,因为这通常是有用的。所以,如果它指向一个已经被释放的内存块,那么你会被通知这个,而且块被释放。同样,如果它应该是刚刚结束堆块,这是数组下标中一个一个错误的常见结果,你会被告知这个事实,以及块分配的地方。如果您使用--read-var-info
选项Memcheck将运行更慢,但可能会更详细地描述任何非法地址。
在这个例子中,Memcheck不能识别地址。实际上地址在堆栈上,但是由于某种原因,这不是有效的堆栈地址 - 它不在堆栈指针下面,这是不允许的。在这种特殊情况下,这可能是由GCC产生的无效代码造成的,这是古代GCC的一个已知bug。
请注意,Memcheck仅告诉您,您的程序即将访问非法地址的内存。它不能阻止访问发生。所以,如果您的程序进行通常会导致分段错误的访问,您的程序仍然会遇到相同的命运 - 但是在此之前您将收到来自Memcheck的消息。在这个特定的例子中,在堆栈上读取垃圾是非致命的,并且程序保持活着。
例如:
有条件的跳跃或移动取决于未初始化的值 在0x402DFA94:_IO_vfprintf(_itoa.h:49) by 0x402E8476:_IO_printf(printf.c:36) 通过0x8048472:main(tests / manuel1.c:8)
当程序使用尚未初始化的值(换句话说,未定义)时,将报告未初始化值使用错误。这里,未定义的值用于printf
C库机械的 某处。运行以下小程序时报告此错误:
int main() { int x; printf(“x =%d \ n”,x); }
重要的是要了解,您的程序可以尽可能多地复制垃圾(未初始化)数据。Memcheck观察这个并且跟踪数据,但不会抱怨。只有当您的程序尝试以可能影响程序外部可见行为的方式使用未初始化的数据时,才会发出投诉。在此示例中,x
未初始化。Memcheck观察到传递给的值_IO_printf
,然后发送给_IO_vfprintf
,但不发表任何评论。但是, _IO_vfprintf
必须检查它的值, x
所以它可以把它变成相应的ASCII字符串,而在这一点Memcheck抱怨。
未初始化数据的来源往往是:
-
程序中的局部变量尚未初始化,如上例所示。
-
在你(或构造函数)之前的堆块(分配给
malloc
,new
或类似的函数)的内容写在那里。
要查看程序中未初始化数据源的信息,请使用该--track-origins=yes
选项。这使得Memcheck运行速度更慢,但可以更容易地追踪未初始化值错误的根本原因。
Memcheck检查系统调用的所有参数:
-
它会自动检查所有直接参数,无论它们是初始化的。
-
此外,如果系统调用需要从程序提供的缓冲区中读取,Memcheck会检查整个缓冲区是否可寻址,并将其内容初始化。
-
此外,如果系统调用需要写入用户提供的缓冲区,Memcheck会检查缓冲区是否可寻址。
系统调用后,Memcheck更新其跟踪信息,以精确反映系统调用引起的内存状态变化。
以下是两个无效参数的系统调用示例:
#include <stdlib.h> #include <unistd.h> int main(void) { char * arr = malloc(10); int * arr2 = malloc(sizeof(int)); 写(1 / * stdout * /,arr,10); 出口(ARR2 [0]); }
你得到这些投诉...
Syscall param write(buf)指向未初始化的字节 在0x25A48723:__write_nocancel(在/lib/tls/libc-2.3.3.so) by 0x259AFAD3:__libc_start_main(in /lib/tls/libc-2.3.3.so) 由0x8048348 :(在/auto/homes/njn25/grind/head4/a.out中) 地址0x25AB8028是大小为10的块内的0个字节 在0x259852B0:malloc(vg_replace_malloc.c:130) 由0x80483F1:主(ac:5) Syscall param exit(error_code)包含未初始化的字节 在0x25A21B44:__GI__exit(在/lib/tls/libc-2.3.3.so) 通过0x8048426:main(ac:8)
因为程序具有(a)从堆块将未初始化的垃圾写入标准输出,并且(b)将未初始化的值传递给exit
。请注意,第一个错误是指由buf
(不是 buf
本身)指向的内存 ,但是第二个错误直接指向了exit
参数 arr2[0]
。
例如:
无效() 在0x4004FFDF:free(vg_clientmalloc.c:577) 通过0x80484C7:main(tests / doublefree.c:10) 地址0x3807F7B4是一个大小为177的空闲块内的0个字节 在0x4004FFDF:free(vg_clientmalloc.c:577) 通过0x80484C7:main(tests / doublefree.c:10)
Memcheck使用malloc
/ 跟踪程序分配的块new
,所以它可以准确地知道free
/ / 的参数 delete
是否合法。这里,这个测试程序已经释放了同一个程序段两次。与非法读/写错误一样,Memcheck尝试了解地址的释放。如果在这里,地址是先前被释放的地址,那么你会被告知 - 同一块的重复释放容易发现。如果您尝试释放不指向堆块开始的指针,您还将收到此消息。
在以下示例中,分配的块 new[]
错误地被释放 free
:
free()/ delete / delete [] 在0x40043249:free(vg_clientfuncs.c:171) by 0x4102BB4E:QGArray ::〜QGArray(void)(tools / qgarray.cpp:149) 由0x4C261C41:PptDoc ::〜PptDoc(void)(include / qmemarray.h:60) 通过0x4C261F0E:PptXml ::〜PptXml(void)(pptxml.cc:44) 地址0x4BB292A8是一个大小为64个alloc'd的块内的0个字节 在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) 通过0x4C21788F:OLEFilter :: convert(QCString const&)(olefilter.cc:272)
在C++
它与它是如何分配兼容的方式来释放内存是很重要的。交易是:
-
如果与分配
malloc
,calloc
,realloc
,valloc
或者memalign
,您必须取消分配free
。 -
如果分配
new
,你必须解除配置delete
。 -
如果分配
new[]
,你必须解除配置delete[]
。
最糟糕的是,在Linux上显然无论如何混合使用,但同样的程序可能会在不同的平台(例如Solaris)上崩溃。所以最好是妥善解决它。根据KDE的人“令人惊奇的是有多少C ++程序员不知道这个”。
要求背后的原因如下。在某些C ++实现中,delete[]
必须使用分配的对象,new[]
因为在指针实际返回之前,编译器会将数组的大小和指针成员存储到数组的内容的析构函数中。 delete
不解决这个问题,会混淆,可能会破坏堆。
下面的C库函数从一个存储器块复制一些数据到另一个(或类似的东西): , memcpy
, strcpy
, strncpy
, 。strcat
strncat
由他们src
和 dst
指针指向的块不允许重叠。POSIX标准具有“如果在重叠的对象之间进行复制,行为未定义”。因此,Memcheck检查这一点。
例如:
== 27492 == memcpy中的源和目的地重叠(0xbffff294,0xbffff280,21) == 27492 == 0x40026CDC:memcpy(mc_replace_strmem.c:71) == 27492 == by 0x804865A:main(overlap.c:40)
您不希望两个块重叠,因为其中一个块可能被复制部分覆盖。
你可能会认为,Memcheck在dst
小于等于 的情况下被过度迂回地报告src
。例如,明显的实现方式memcpy
是从第一个字节到最后一个字节的复制。但是,某些架构的优化指南建议从最后一个字节复制到第一个。而且,复制前的一些实现memcpy
为零 dst
,因为归零目的地的高速缓存行可以提高性能。
故事的道德是:如果你想写真正的便携式代码,不要对语言实现做任何假设。