一、valgrind简介

Valgrind是一款用于内存调试、内存泄漏检测以及性能分析、检测线程错误的软件开发工具。

Valgrind一般包含下列工具:

1.Memcheck

最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。所以,它能检测以下问题:

1.对未初始化内存的使用;

2.读/写释放后的内存块;

3.读/写超出malloc分配的内存块;

4.读/写不适当的栈中内存块;

5.内存泄漏,指向一块内存的指针永远丢失;

6.不正确的malloc/free或new/delete匹配;

7,memcpy()相关函数中的dst和src指针重叠。

这些问题往往是C/C++程序员最头疼的问题,Memcheck在这里帮上了大忙。

memcheck 工具的常用选型

1、leak-check

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

用于控制内存泄漏检测力度。

no,不检测内存泄漏;

summary,仅报告总共泄漏的数量,不报告具体泄漏位置;

yes/full,报告泄漏总数、泄漏的具体位置。

2、show-reachable

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

用于控制是否检测控制范围之外的泄漏,比如全局指针、static指针等。

3、undef-value-errors

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

用于控制是否检测代码中使用未初始化变量的情况。

4、其他选项

--log-file=filename 将结果输出到文件。

--log-socket=192.168.0.1:12345 输出到网络。

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

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

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

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

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

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

内存检查选项:

--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的一个部分,它并不检查未定义值的错误。使用这个选项,如果你不希望看到未定义值错误。

2.Callgrind

和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

3.Cachegrind

Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

4.Helgrind

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

5. Massif

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

此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具。

二、安装valgrind

wget https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2

:~/Download$ tar -xjvf valgrind-3.15.0.tar.bz2

:~/Download$ cd valgrind-3.15.0/

因为valgrind支持多个平台,根据当前主机配置valgrind

:~/Download/valgrind-3.15.0$ ./configure

./configure之后就有makefile出现,接着就是make编译,安装

:~/Download/valgrind-3.15.0$ make

:~/Download/valgrind-3.15.0$ sudo make install

查看一下版本看看是否安装好

:~/Download/valgrind-3.15.0$ valgrind --version

valgrind-3.15.0

三、valgrind使用

1、内存泄漏

#include <stdio.h>
#include <stdlib.h>void fun(void)
{int *x = malloc(10*sizeof(int));x [10] = 0; //问题1:堆块溢出
} //问题2:内存泄漏 -  x未释放int main(int argc, char **argv)
{fun() ;return 0 ;
}

编译程序-g以包含调试信息,以便Memcheck的错误消息包含确切的行号。

:~/c_code$ gcc -g valgrind_test.c -o valgrind_test

正常运行

:~/c_code$ ./valgrind_test

Memcheck是默认工具。--leak-check 选项打开详细的内存泄漏检测器。程序运行速度会比正常情况慢很多(例如20到30倍),并且会占用更多内存。Memcheck将发出有关内存错误和检测到的泄漏的消息。

:~/c_code$ valgrind --leak-check=yes ./valgrind_test

一开始是valgrind信息“==62414==”表示进程号

==62414== Memcheck, a memory error detector

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

==62414== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info

==62414== Command: ./valgrind_test

程序访问非法地址的内存,无效写入

==62414== Invalid write of size 4

==62414== at 0x40054B: fun (valgrind_test.c:21)

==62414== by 0x400566: main (valgrind_test.c:28)

==62414== Address 0x5201068 is 0 bytes after a block of size 40 alloc'd

==62414== at 0x4C2AEC3: malloc (vg_replace_malloc.c:309)

==62414== by 0x40053E: fun (valgrind_test.c:20)

==62414== by 0x400566: main (valgrind_test.c:28)

==62414==

堆区情况:

==62414== HEAP SUMMARY:

==62414== in use at exit: 40 bytes in 1 blocks

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

内存泄漏消息如下所示:

==62414== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1

==62414== at 0x4C2AEC3: malloc (vg_replace_malloc.c:309)

==62414== by 0x40053E: fun (valgrind_test.c:20)

==62414== by 0x400566: main (valgrind_test.c:28)

有几种泄漏; 两个最重要的类别是,肯定泄露(definitely lost),可能已经泄露(possibly lost)

==62414== LEAK SUMMARY:

==62414== definitely lost: 40 bytes in 1 blocks

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

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

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

==62414== suppressed: 0 bytes in 0 blocks

==62414==

==62414== For lists of detected and suppressed errors, rerun with: -s

==62414== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

2线程锁

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>void * thread_worker1(void *args) ;typedef struct worker_ctx_s
{int             shared_var  ;pthread_mutex_t lock ;
} worker_ctx_t ;void *thread_worker1(void *args)
{worker_ctx_t    *ctx = (worker_ctx_t *)args ;if(!args){printf("%s() get invalid arguments\n", __FUNCTION__) ; // __FUNCTION__ get function namepthread_exit(NULL) ; }printf("Thread worker1 [%ld] start running...\n", pthread_self()) ; /* 两次上锁导致死锁  */pthread_mutex_lock(&(ctx->lock)) ;pthread_mutex_lock(&(ctx->lock)) ;  pthread_mutex_unlock( &ctx->lock ) ;sleep(1) ; printf("Thread worker1 exit...\n") ;pthread_exit(NULL) ;return NULL ;
}int main(int argc, char **argv)
{ worker_ctx_t    worker_ctx ;pthread_t       tid ; pthread_attr_t  thread_attr ;worker_ctx.shared_var = 1000 ;pthread_mutex_init(&worker_ctx.lock, NULL) ; if ( pthread_attr_init(&thread_attr) != 0 ){printf("pthread_attr_init() failed: %s\n", strerror(errno)) ;return -1 ;}if ( pthread_attr_setstacksize( &thread_attr, 1024*120) != 0 ){printf("pthread_setstacksize() failure: %s\n", strerror(errno) ) ;return -2 ;}if ( pthread_attr_setdetachstate(&thread_attr,  PTHREAD_CREATE_DETACHED) != 0 ){printf("pthread_attr_setdetachstate() error: %s\n", strerror(errno)) ;return -3 ;}/*    Create thread    */ pthread_create(&tid, &thread_attr,  thread_worker1, &worker_ctx);if ( pthread_attr_destroy(&thread_attr) != 0 ) {printf("pthread_attr_destroy() failed :%s\n", strerror(errno)) ;return -4 ;}sleep(5) ;return 0 ;
}

:~/c_code$ gcc -g valgrind_test_helgrand.c -o valgrind_test_helgrand -lpthread

:~/c_code$ valgrind --tool=helgrind ./valgrind_test_helgrand

==63432== Helgrind, a thread error detector

==63432== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al.

==63432== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info

==63432== Command: ./valgrind_test_helgrand

程序打印的消息

Thread worker1 [67376896] start running...

==63432== ---Thread-Announcement------------------------------------------

两个线程被创建

==63432== Thread #2 was created

==63432== at 0x5158FFE: clone (clone.S:74)

==63432== by 0x4E44199: do_clone.constprop.3 (createthread.c:75)

==63432== by 0x4E458BA: create_thread (createthread.c:245)

==63432== by 0x4E458BA: pthread_create@@GLIBC_2.2.5 (pthread_create.c:611)

==63432== by 0x4C31CBA: pthread_create_WRK (hg_intercepts.c:427)

==63432== by 0x4C32D98: pthread_create@* (hg_intercepts.c:460)

==63432== by 0x400CA2: main (valgrind_test_helgrand.c:84)

==63432==

==63432== ----------------------------------------------------------------

尝试重新锁定本身已持有的非递归锁

==63432== Thread #2: Attempt to re-lock a non-recursive lock I already hold

==63432== at 0x4C2F259: mutex_lock_WRK (hg_intercepts.c:899)

==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)

==63432== by 0x400B5C: thread_worker1 (valgrind_test_helgrand.c:44)

==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)

==63432== by 0x4E45183: start_thread (pthread_create.c:312)

==63432== by 0x515903C: clone (clone.S:111)

锁之前已经获取过

==63432== Lock was previously acquired

==63432== at 0x4C2F320: mutex_lock_WRK (hg_intercepts.c:909)

==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)

==63432== by 0x400B4C: thread_worker1 (valgrind_test_helgrand.c:43)

==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)

==63432== by 0x4E45183: start_thread (pthread_create.c:312)

==63432== by 0x515903C: clone (clone.S:111)

==63432==

==63432== ----------------------------------------------------------------

退出线程时任保持一个锁

==63432== Thread #2: Exiting thread still holds 1 lock

==63432== at 0x4E4BF1C: __lll_lock_wait (lowlevellock.S:135)

==63432== by 0x4E47648: _L_lock_909 (pthread_mutex_lock.c:151)

==63432== by 0x4E4746E: pthread_mutex_lock (pthread_mutex_lock.c:79)

==63432== by 0x4C2F2BB: mutex_lock_WRK (hg_intercepts.c:902)

==63432== by 0x4C3317D: pthread_mutex_lock (hg_intercepts.c:925)

==63432== by 0x400B5C: thread_worker1 (valgrind_test_helgrand.c:44)

==63432== by 0x4C31EAE: mythread_wrapper (hg_intercepts.c:389)

==63432== by 0x4E45183: start_thread (pthread_create.c:312)

==63432== by 0x515903C: clone (clone.S:111)

==63432==

==63432==

==63432== Use --history-level=approx or =none to gain increased speed, at

==63432== the cost of reduced accuracy of conflicting-access information

==63432== For lists of detected and suppressed errors, rerun with: -s

==63432== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 26 from 25)

valgrind小结相关推荐

  1. linux注入内存泄露,Linux 内存泄露小结

    本文仅限记录自己的一次 内存泄露追踪小记. 可能并不十分适用与大家的情况.而且方法也并不是很smart.仅做记录,能提供个思路更好. 一. 要问调试程序遇到什么问题最头疼, 内存泄露肯定能排在前几名里 ...

  2. 遇到一个valgrind自身的bug

    背景 公司C++项目代码使用了cppcheck做静态代码检查,也使用valgrind检查是否有内存泄漏问题.我多次强调要做到0警告,虽然有了CICD的Jenkins自动检查,也将结果通过邮件发给项目人 ...

  3. C/C++之内存调试工具GDB与Valgrind

    0 背景 写 C++的同学想必有太多和内存打交道的血泪经验了,常常被 C++的内存问题搅的焦头烂额.(写 core 的经验了)有很多同学一见到 core 就两眼一抹黑,不知所措了.笔者 入" ...

  4. 记一次使用Valgrind查找解决内存问题的玄幻旅程

    文章目录 前言 玄幻旅途 故事背景 初入泥潭 一片混沌 追根溯源 抽丝剥茧 大海捞针 祭出法宝 屏蔽无关 移形换位 再请法宝 风平浪静 若有所思 参考文章 前言 看题目来说这应该是一篇教程式文章,但为 ...

  5. 代码测试,调试与优化小结

    http://www.cppblog.com/cuijixin/default.html?page=8 代码测试.调试与优化的小结 by falcon<zhangjinw@gmail.com&g ...

  6. GJM :C++ 资源收集小结

    c++ 资源收集小结 阅读目录 标准库 框架 人工智能 异步事件循环 音频 生态学 压缩 并发性 容器 密码学 数据库 调试 游戏引擎 图形用户界面 图形 图像处理 国际化 Jason 日志 机器学习 ...

  7. valgrind基本功能介绍、基础使用方法说明

    1.Valgrind概述 Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合. Valgrind由内核(core)以及基于内核的其他调试工具组成.内核类似于一个框架(f ...

  8. Linux 命令(143)—— valgrind 命令

    文章目录 1.命令简介 2.命令格式 3.选项说明 4.常用示例 4.1 内存泄漏 4.2 内存越界 4.3 内存覆盖 4.4 使用未初始化的值 4.5 内存申请与释放函数不匹配 5.小结 参考文献 ...

  9. 用valgrind 检测内存错误

    用valgrind 检测内存错误 ---------------------------------------- 前言: -------------------------------------- ...

最新文章

  1. SAP登陆界面TITLE修改方法(Method of SAP Logon GUI Title Modification)
  2. python如何实现办公自动化培训_基于python实现自动化办公学习笔记(CSV、word、Excel、PPT)...
  3. 旅途中的人物之一:卖菜老人
  4. javasrcipt的作用域和闭包(二)续篇之:函数内部提升机制与Variable Object
  5. shader 反射 水面_2D水面波光效果,以及一些2D常用shader的实现
  6. 关于使用SharedObject来缓存游戏图片或者swf文件资源到客户端本地的研究
  7. Dijstra算法-------为了纪念,等以后看的时候方便
  8. apache camel 相关配置_Web基础配置篇(二): Maven配置及使用
  9. 全面综合的管理平台,让所有网络都有管理员
  10. 调用阿里API获取城市天气信息
  11. 关于Linux运行steam的解决方案
  12. windows 下 c++ 二维码生成库
  13. 你会卖掉自己的网上信息吗?大数据可能根本不属于你
  14. 12306 抢票软件已被限制
  15. php qcloud sdk weapp_qcloud/
  16. 小议关键字del与实例方法__del__(self)
  17. 基础算法:斐波那契函数学习
  18. 单页网站快速创富秘籍
  19. 迅雷链接转为普通链接(js实现)
  20. vim编辑器如何退出

热门文章

  1. c语言十进制转八进制递归,C语言之利用递归将十进制转换为二进制
  2. nginx开启日志和配置日志文件路径
  3. 从教20年随笔——4409的归来
  4. RAID管理工具 -- MegaCli常用命令
  5. 注意力机制 | CNN-LSTM-Attention基于卷积-长短期记忆网络结合注意力机制多输入单输出回归预测(Matlab程序)
  6. Java程序员11面阿里,错失offer,期间还面了EMC+网易+美团......
  7. 模拟量采集软件虚拟精度提升方案
  8. python 延时sleep
  9. Java实现 幸运数字
  10. 攻防世界 forgot