valgrind内存泄漏检测快速上手

valgrind内存泄漏检测快速上手,第1张

通过 -g 编译程序来携带debug信息,这样子输出的错误信息就可以包含精确的行号。如果你可衡拦以承受程序运行缓慢,那么我们可以使用 -O0 来编译程序。如果使用 -O1 ,那么输出的行号可能会不准确。不推荐使用 -O2 及以上,因为 valgrind memcheck 偶尔会报告不存在茄纳的未初始化值错误。

如果平时这么运行

就使用这个命令

Memcheck 是默认的 valgrind 工具, --leak-check 打开了内存泄漏检测开关。

通过这个命令运行,大约会比平时运行慢20到30倍,并且使用更大的内咐纳胡存。Memcheck 将发出它检测到的内存错误和泄漏的信息。

使用样例的C程序,包含一个内存分配错误和内存溢出

执行内存检测

输出信息如下

堆栈可以表明什么内存泄露了,但 memcheck 并不能告诉你内存泄漏的原因

valgrind会提示两种内存泄漏

https://www.valgrind.org/docs/manual/quick-start.html#quick-start.intro

https://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.errormsgs

可以使用Valgrind工具 Valgrind包括如下一些工具:

Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。

Callgrind。它主要用来检查程序中函数调用过程中出现的问题。

Cachegrind。它主要用来检查程序中缓存使用出现的问题。

Helgrind。它主要用来检查多线程程序中出现的竞争问题。

Massif。它主要用来检查程序中堆栈使用中出现的问题。

Extension。可以利用core提供的功能,自己编写特定的内存调试工具

Valgrind 使用

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

-tool=<name>最常用的选项。运行 valgrind中名为toolname的工具。默认memcheck。

h –help 显示帮助信息。

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

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

v –verbose 更详细的信息, 增加错误数统计。

-trace-children=no|yes 跟踪子线程? [no]

-track-fds=no|yes 跟踪打开的文件描述?[no]

-time-stamp=no|yes 增加时间戳到LOG信息? [no]

-log-fd=<number>输出LOG到描述符文件 [2=stderr]

-log-file=<file>将输出的信息写入到filename.PID的文件里,PID是运行程序的进行ID

-log-file-exactly=<file>输出LOG信息到 file

-log-file-qualifier=<VAR>取得环境变量的值来做为输出信息的文件名。 [none]

-log-socket=ipaddr:port 输出LOG到socket ,ipaddr:port

LOG信息输出

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

-num-callers=<number>show <number>callers in stack traces [12]

-error-limit=no|yes 如果太多错误,则停止显示新错误? [yes]

-error-exitcode=<number>如果发现错误则返回错误代码 [0=disable]

-db-attach=no|yes 当出现错误,valgrind会自动启动调试器gdb。[no]

-db-command=<command>启动调试器的命令行选项[gdb -nw %f %p]

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

-leak-check=no|summary|full 要求对leak给出详细信息? [summary]

-leak-resolution=low|med|high how much bt merging in leak check [low]

-show-reachable=no|yes show reachable blocks in leak check? [no]

Valgrind 使用举例(一)

下面是一段有问题的C程序代码test.c

#i nclude <stdlib.h>

void f(void)

{

  int* x = malloc(10 * sizeof(int))

  x[10] = 0 //问厅基题1: 数组下标越界

}                  //问题2: 内存没有释放

int main(void)

{

  f()

  return 0

}

1、 编译程序test.c

gcc -Wall test.c -g -o test

2、 使用Valgrind检查程序BUG

valgrind --tool=memcheck --leak-check=full ./test

使用未初始化内存问题

问题分析:

对于位于程序中不同段的变量,其初始值是不同的,全局凳团变量和静态变量初始值为0,而局部变量和动态申请的变量,其初始值为随机值。如果程序使用了为随机值的变量,那么程序的行为就变得不可预期。

下面的程序就是一种常见的,使用了扮粗谨未初始化的变量的情况。数组a是局部变量,其初始值为随机值,而在初始化时并没有给其所有数组成员初始化,如此在接下来使用这个数组时就潜在有内存问题。

结果分析:

假设这个文件名为:badloop.c,生成的可执行程序为badloop。用memcheck对其进行测试,输出如下。

输出结果显示,在该程序第11行中,程序的跳转依赖于一个未初始化的变量。准确的发现了上述程序中存在的问题。

内存读写越界

问题分析:

这种情况是指:访问了你不应该/没有权限访问的内存地址空间,比如访问数组时越界;对动态内存访问时超出了申请的内存大小范围。下面的程序就是一个典型的数组越界问题。pt是一个局部数组变量,其大小为4,p初始指向pt数组的起始地址,但在对p循环叠加后,p超出了pt数组的范围,如果此时再对p进行写 *** 作,那么后果将不可预期。

结果分析:

假设这个文件名为badacc.cpp,生成的可执行程序为badacc,用memcheck对其进行测试,输出如下。

 

输出结果显示,在该程序的第15行,进行了非法的写 *** 作;在第16行,进行了非法读 *** 作。准确地发现了上述问题

使用 Valgrind Memcheck

memcheck工具的使用方式如下:

valgrind --tool=memcheck ./a.out

从上面的命令可以清楚的看到, 主要的命令是valgrind,而我们想使用的工具是通过'-tool'选项来指定的. 上面的‘a.out’指的是我们想使用memcheck运行的可执行文件.

该工具可以检做腔肆测下列与内存相关的问题 :

未释放内存的使用

对释放后内存的读/写

对已分配内存块尾部的读/写

内存泄露

不匹配的使用malloc/new/new[] 和 free/delete/delete[]

重复释放内存

注意: 上面列出的并不很全面,但却包含了能被该工具检测到的很多普遍的问题.

让我们一个一个地对上面的场景进行讨论:

注意: 下面讨论的所有测试代码都应该使用gcc并且加上-g选圆租项(用来在memcheck的输出中生成行号)进行编译. 就想我们之前讨论过的 C程序被编译成可执行文件, 它需要经历四个不同的阶段.

ToB蓝波湾

翻译于 1 年 前

0人顶

顶 翻译的不错哦!

1. 使用未初始化的内存

Code :

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

char *p

char c = *p

printf("\n [%c]\n",c)

return 0

}

在上面的代码中,我们尝试使用未初始化的指针 ‘p’.

让我们运行Memcheck来看下结果.

$ valgrind --tool=memcheck ./val

==2862== Memcheck, a memory error detector

==2862== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.

==2862== Using Valgrind-3.6.0.SVN-Debian and LibVEXrerun with -h for copyright info

==2862== Command: ./val

==2862==

==2862== Use of uninitialised value of size 8

==2862==at 0x400530: main (valgrind.c:8)

==2862==

[#]

==2862==

==2862== HEAP SUMMARY:

==2862== in use at exit: 0 bytes in 0 blocks

==2862== total heap usage: 0 allocs, 0 frees, 0 bytes allocated

==2862==

==2862== All heap blocks were freed -- no leaks are possible

==2862==

==2862== For counts of detected and suppressed errors, rerun with: -v

==2862== Use --track-origins=yes to see where uninitialized values come from

==2862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

从上面的输出可以看到,Valgrind检测到了未初始化的变量,然后给出了警告(上面加粗的几行(译者注:貌似上面没有加粗的)).

2. 在内存被释放后进行读/写

Code :

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

char *p = malloc(1)

*p = 'a'

char c = *p

printf("\n [%c]\n",c)

free(p)

c = *p

return 0

}

上面的代码中,我们纯轿有一个释放了内存的指针 ‘p’ 然后我们又尝试利用指针获取值.

让我们运行memcheck来看一下Valgrind对这种情况是如何反应的.

$ valgrind --tool=memcheck ./val

==2849== Memcheck, a memory error detector

==2849== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.

==2849== Using Valgrind-3.6.0.SVN-Debian and LibVEXrerun with -h for copyright info

==2849== Command: ./val

==2849==

[a]

==2849== Invalid read of size 1

==2849==at 0x400603: main (valgrind.c:30)

==2849== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd

==2849==at 0x4C270BD: free (vg_replace_malloc.c:366)

==2849==by 0x4005FE: main (valgrind.c:29)

==2849==

==2849==

==2849== HEAP SUMMARY:

==2849== in use at exit: 0 bytes in 0 blocks

==2849== total heap usage: 1 allocs, 1 frees, 1 bytes allocated

==2849==

==2849== All heap blocks were freed -- no leaks are possible

==2849==

==2849== For counts of detected and suppressed errors, rerun with: -v

==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

从上面的输出内容可以看到,Valgrind检测到了无效的读取 *** 作然后输出了警告 ‘Invalid read of size 1′.

另注,使用gdb来调试c程序.

3. 从已分配内存块的尾部进行读/写

Code :

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

char *p = malloc(1)

*p = 'a'

char c = *(p+1)

printf("\n [%c]\n",c)

free(p)

return 0

}

在上面的代码中,我们已经为‘p’分配了一个字节的内存,但我们在将值读取到 ‘c’中的时候使用的是地址p+1.

现在我们使用Valgrind运行上面的代码 :

$ valgrind --tool=memcheck ./val

==2835== Memcheck, a memory error detector

==2835== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.

==2835== Using Valgrind-3.6.0.SVN-Debian and LibVEXrerun with -h for copyright info

==2835== Command: ./val

==2835==

==2835== Invalid read of size 1

==2835==at 0x4005D9: main (valgrind.c:25)

==2835== Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd

==2835==at 0x4C274A8: malloc (vg_replace_malloc.c:236)

==2835==by 0x4005C5: main (valgrind.c:22)

==2835==

[]

==2835==

==2835== HEAP SUMMARY:

==2835== in use at exit: 0 bytes in 0 blocks

==2835== total heap usage: 1 allocs, 1 frees, 1 bytes allocated

==2835==

==2835== All heap blocks were freed -- no leaks are possible

==2835==

==2835== For counts of detected and suppressed errors, rerun with: -v

==2835== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

同样,该工具在这种情况下也检测到了无效的读取 *** 作.

4. 内存泄露

Code:

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

char *p = malloc(1)

*p = 'a'

char c = *p

printf("\n [%c]\n",c)

return 0

}

在这次的代码中, 我们申请了一个字节但是没有将它释放.现在让我们运行Valgrind看看会发生什么:

$ valgrind --tool=memcheck --leak-check=full ./val

==2888== Memcheck, a memory error detector

==2888== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.

==2888== Using Valgrind-3.6.0.SVN-Debian and LibVEXrerun with -h for copyright info

==2888== Command: ./val

==2888==

[a]

==2888==

==2888== HEAP SUMMARY:

==2888== in use at exit: 1 bytes in 1 blocks

==2888== total heap usage: 1 allocs, 0 frees, 1 bytes allocated

==2888==

==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1

==2888==at 0x4C274A8: malloc (vg_replace_malloc.c:236)

==2888==by 0x400575: main (valgrind.c:6)

==2888==

==2888== LEAK SUMMARY:

==2888==definitely lost: 1 bytes in 1 blocks

==2888==indirectly lost: 0 bytes in 0 blocks

==2888== possibly lost: 0 bytes in 0 blocks

==2888==still reachable: 0 bytes in 0 blocks

==2888== suppressed: 0 bytes in 0 blocks

==2888==

==2888== For counts of detected and suppressed errors, rerun with: -v

==2888== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

输出行(上面加粗的部分)显示,该工具能够检测到内存的泄露.


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/8180283.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存