参数配置

gcc

  • -g: 增加调试信息,供valgrind精确定位。
  • -O0:关闭gcc优化;优化产生的代码可能会造成valgrind误判。

valgrind

  • --leak-check=full

    no: 不进行内存泄露检测; summary: 显示内存泄露情况; full:不仅显示内存泄露,还显示出错代码。

  • --show-reachable=yes

    详细显示still reachable 和 indirectly lost两种类型的内存泄露,默认不显示;如case1和case4。

内存泄露

内存泄露: 由于疏忽或错误造成程序未能释放已经不能再使用的内存。 —— 维基百科

指针

start-pointer: 指向内存起始位置 
interior-pointer: 指向内存中间位置

泄露类型

possibly lost: 指针指向内存的内部位置。

still reachable: 程序运行结束后,虽然没有被释放,但仍然可以访问。

definitely lost:内存无法被访问。

indirectly lost:虽然有地址指向该空间,但已经无法被访问了。

泄露举例

     Pointer chain            AAA Leak Case   BBB Leak Case
------------- ------------- -------------
(1) RRR ------------> BBB DR
(2) RRR ---> AAA ---> BBB DR IR
(3) RRR BBB DL
(4) RRR AAA ---> BBB DL IL
(5) RRR ------?-----> BBB (y)DR, (n)DL
(6) RRR ---> AAA -?-> BBB DR (y)IR, (n)DL
(7) RRR -?-> AAA ---> BBB (y)DR, (n)DL (y)IR, (n)IL
(8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
(9) RRR AAA -?-> BBB DL (y)IL, (n)DL Pointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointer Leak Case legend:
- DR: Directly reachable
- IR: Indirectly reachable
- DL: Directly lost
- IL: Indirectly lost
- (y)XY: it's XY if the interior-pointer is a real pointer
- (n)XY: it's XY if the interior-pointer is not a real pointer
- (_)XY: it's XY in either case

case1: RRR ---> BBB

void *RRR;
int main()
{
RRR = malloc(8);
return 0;
}

==1244== LEAK SUMMARY: 
==1244==  still reachable: 8 bytes in 1 blocks

case2: RRR ---> AAA ---> BBB

void **RRR;
int main()
{
RRR = (void**)malloc(8);
*RRR = malloc(8);
return 0;
}

==1345== LEAK SUMMARY: 
==1345==  still reachable: 16 bytes in 2 blocks

case3:RRR    BBB

int main()
{
void *RRR = malloc(8);
return 0;
}

==1400== LEAK SUMMARY: 
==1400==  definitely lost: 8 bytes in 1 blocks

case4:RRR   AAA ---> BBB

int main()
{
void **RRR = (void**)malloc(8);
*RRR = malloc(8);
return 0;
}

==1461== LEAK SUMMARY: 
==1461==  definitely lost: 8 bytes in 1 blocks 
==1461==  indirectly lost: 8 bytes in 1 blocks

case5:RRR -?-> BBB

void *RRR;
int main()
{
RRR = malloc(8);
RRR = (char*)RRR + 2;
return 0;
}

==1530== LEAK SUMMARY: 
==1530==  possibly lost: 8 bytes in 1 blocks

case6:RRR ---> AAA -?-> BBB

void **RRR;
int main()
{
RRR = (void**)malloc(8);
*RRR = malloc(8);
*RRR = (char*)(*RRR) + 2; return 0;
}

==1587== LEAK SUMMARY: 
==1587==  possibly lost: 8 bytes in 1 blocks 
==1587==  still reachable: 8 bytes in 1 blocks

case7:RRR -?-> AAA ---> BBB

void **RRR;
int main()
{
RRR = (void**)malloc(8);
*RRR = malloc(8);
RRR = (void**)((char*)RRR + 1);
return 0;
}

==1642== LEAK SUMMARY: 
==1642==  possibly lost: 16 bytes in 2 blocks

case8:RRR -?-> AAA -?-> BBB

void **RRR;
int main()
{
RRR = (void**)malloc(8);
*RRR = malloc(8); *RRR = ((char*)(*RRR) + 1);
RRR = (void**)((char*)RRR + 1);
return 0;
}

==1776== LEAK SUMMARY: 
==1776==  possibly lost: 16 bytes in 2 blocks

case9:RRR    AAA -?-> BBB

int main()
{
void **RRR = (void**)malloc(8);
*RRR = malloc(8); *RRR = ((char*)(*RRR) + 1);
return 0;
}

==3856== LEAK SUMMARY: 
==3856==  definitely lost: 8 bytes in 1 blocks 
==3856==  indirectly lost: 8 bytes in 1 blocks

内存错误

读写越界

 8 int *arr = new int[2];
9 arr[2] = 2;
10 arr[0] = arr[2];
11
12 delete [] arr;

==2371== Invalid write of size 4 
==2371==  at 0x80485FD: main (test.cpp:9) 
==2371== 
==2371== Invalid read of size 4 
==2371==  at 0x8048607: main (test.cpp:10)

NOTE: 如果在栈空间上申请数组arr, valgrind没有检测出读写越界。详见原理简述

地址重叠

 8 char *str = new char[10];
9 char *src = str;
10 char *dst = str + 2;
11 memcpy(dst, src, 4);
12
13 delete [] str;

==2413== Source and destination overlap in memcpy(0x433902a, 0x4339028, 4) 
==2413==  at 0x402EE13: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==2413==  by 0x8048654: main (test.cpp:11)

多次释放

 8  char *str = new char[10];
9 char *rep = str;
10
11 delete [] str;
12 delete [] rep;

==2440== Invalid free() / delete / delete[] / realloc() 
==2440==  at 0x402BD38: operator delete 
==2440==  by 0x8048623: main (test.cpp:12) 
==2440== Address 0x4339028 is 0 bytes inside a block of size 10 free’d 
==2440==  at 0x402BD38: operator delete 
==2440==  by 0x8048610: main (test.cpp:11) 
==2440== HEAP SUMMARY: 
==2440==  in use at exit: 0 bytes in 0 blocks 
==2440==  total heap usage: 1 allocs, 2 frees, 10 bytes allocated

分配尺寸错误

 9  int *arr = new int[-1];
10 delete [] arr;

==2496== Argument ‘size’ of function __builtin_vec_new has a fishy (possibly negative) value: -1 
==2496==  at 0x402ADFC: operator new (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==2496==  by 0x80485F1: main (test.cpp:9)

释放内部指针

int main()
{
void *RRR = malloc(10);
RRR = (char*)RRR + 1; free(RRR);
}

==3953== HEAP SUMMARY: 
==3953== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1 
==3953==  at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==3953==  by 0x8048461: main (inter.cpp:5) 
==3953== 
==3953== LEAK SUMMARY: 
==3953==  definitely lost: 10 bytes in 1 blocks

分配与释放函数不一致

 9 int *arr = new int[10];
10
11 free(arr);

==2519== Mismatched free() / delete / delete [] 
==2519==  at 0x402B3D8: free 
==2519==  by 0x8048601: main (test.cpp:11) 
==2519== Address 0x4339028 is 0 bytes inside a block of size 40 alloc’d 
==2519==  at 0x402ADFC: operator new 
==2519==  by 0x80485F1: main (test.cpp:9)

原理简述

valgrind memcheck 在内部模拟一个CPU环境,所有的数据处理流程,都在内部CPU进行模拟。

valid-value (V) bits

valgrind为程序数据流中的每个bit都关联了一个valid-value bit(简称V),用来检测该bit是否有效。比如int val = 4,当CPU从内存中加载val时,会同时从valid-value位图中读取32个bit;赋值操作发生时,该32个valid-value bit被设置为有效;当变量回写内存时,valid-value也会同时被写入内存。

NOTE: 数据中每个bit都会有一个V bit 与之关联,而不是对每个字节进行监控。X = X | (1 << 3) 只对某个bit赋值。

V 设置时机 
变量被赋值时。

V 检测时机

  • 变量值用于生产地址
  • 变量值参影响程序运行流
  • 变量值用于系统调用

case1

int main()
{
int *p;
int result = *p;
}

==4413== Use of uninitialised value of size 4 
==4413==  at 0x80483F6: main (address.cpp:6)

case2

int main()
{
int result;
if (result == 6)
{
cout << "got" << endl;
}
}

==4436== Conditional jump or move depends on uninitialised value(s) 
==4436==  at 0x80486BB: main (address.cpp:8)

如果未初始化变量不进行分支决策,则不会报错。

int main()
{
int arr[2];
int result; for (size_t i = 0; i < 2; i++)
{
result += arr[i];
} return 0;
}

==1169== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

case3

int main()
{
int result;
exit(result);
}

==4450== Syscall param exit_group(status) contains uninitialised byte(s) 
==4450==  at 0x41DBBD4: _Exit (_exit.S:33)

疑问: 为什么不是每次使用变量的时候,都进行有效检测? 
解释: 正如官方文档所说,C语言中涉及内存地址对齐

struct S { int x; char c};

struct S s1, s2;
s1.x = 0;
s1.c = ' '; s2 = s1;

结构体S涉及内存对齐,大小为8个字节。s1中,末尾的3个字节V始终是无效,如果在赋值的时候也进行检测,则会报错,实际上却没有必要。

valid-address (A) bits

valgrind为程序中的每个地址都关联了一个valid-address(简称A),用于检测地址是否有效。每个字节对应一个V bit。

A bit 设置时机

  • 程序启动时,全局数据区域设置为可访问。
  • 调用new/malloc时,将分配空间设置为可访问;delete/free时,设置为不可访问。
  • 函数调用栈,栈指针SPBP之间的位置,被设置为可访问。栈回收时,设置为不可访问。
  • 某些系统调用。如mmap,进行内存映射。

函数栈

int main()
{
int arr[2];
arr[2] = 2;
}

==1125== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 
NOTE: 栈上的读写越界检测不出来

A bit 检测时机 
当读写内存时,查询A bit,检测读写地址是否有效。

V & A

V: 数据是否有效 
A: 地址是否有效

NOTE: calloc 会同时设置A & V bit,calloc返回后,将区间内容设置为0。

valgrind使用的更多相关文章

  1. 使用valgrind检查内存

    Valgrind是运行在Linux上一套基于仿真技术的程序调试和分析工具,是公认的最接近Purify的产品,它包含一个内核——一个软件合成的CPU,和一系列的小工具,每个工具都可以完成一项任务——调试 ...

  2. 【转】 如何使用Valgrind memcheck工具进行C/C++的内存泄漏检测

    系统编程中一个重要的方面就是有效地处理与内存相关的问题.你的工作越接近系统,你就需要面对越多的内存问题.有时这些问题非常琐碎,而更多时候它会演变成一个调试内存问题的恶梦.所以,在实践中会用到很多工具来 ...

  3. linux下内存泄露检测工具Valgrind介绍

    目前在linux开发一个分析实时路况的应用程序,在联合测试中发现程序存在内存泄露的情况. 这下着急了,马上就要上线了,还好发现了一款Valgrind工具,完美的解决了内存泄露的问题. 推荐大家可以使用 ...

  4. Valgrind 发现程序的内存问题

    参考 : [1]. 应用 Valgrind 发现 Linux 程序的内存问题. http://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/ [2 ...

  5. Valgrind 3.11.0编译安装

    Valgrind 3.11.0编译安装 Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind遵守GNU通用公共许可证条款,是一款自由软件. 到3.3.0版本为 ...

  6. valgrind检查C++内存泄漏

    valgrind --tool=memcheck --leak-check=full ./httptest Valgrind 使用 用法: valgrind [options] prog-and-ar ...

  7. Valgrind的多线程调试工具

    Valgrind的多线程调试工具  Helgrind是Valgrind的一个重点功能 本节主要针对与多线程基本安全问题进行检测:[所有的代码环境都是在POSIX_THREAD模式下] 写线程代码时 经 ...

  8. 交叉编译mips平台上valgrind

    STEP 1:下载最新版本的valgrind:http://www.valgrind.org/downloads/valgrind-3.9.0.tar.bz2 目前支持的平台,在官网上列表如下:{x8 ...

  9. HTTP(socket)下载遇到valgrind提示的错误: Conditional jump or move depends on uninitialised value(s)

    我写了个http下载函数,下载txt.jpg都正常,就是下载php有问题:valgrind会报错Conditional jump or move depends on uninitialised va ...

  10. Valgrind简单用法

    Valgrind的主要作者Julian Seward刚获得了今年的Google-O'Reilly开源大奖之一──Best Tool Maker.让我们一起来看一下他的作品.Valgrind是运行在Li ...

随机推荐

  1. EF core 中用lambda表达式和Linq的一些区别

    转眼一看,又过了10几天没有写博客了,主要还是没有什么可以写的,因为遇到的问题都不是很有价值.不过最近发现用lambda表达式,比用Linq的代码量会少一些,而且也方便一些.不过两者都差不多,相差不是 ...

  2. spring boot 搭建基本套路《1》

    1. Spring复习 Spring主要是创建对象和管理对象的框架. Spring通过DI实现了IoC. Spring能很大程度的实现解耦. 需要掌握SET方式注入属性的值. 需要理解自动装配. 需要 ...

  3. Java中Lambda表达式的简单使用

    Lambda表达式是Java SE 8中一个重要的新特性.你可以把 Lambda表达式 理解为是一段可以传递的代码 (将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风格 ...

  4. MAthJax入门教程(五分钟上手)

    最近在研究,在页面中显示一些数学公式.搞得我很头疼. 据说MathJax会统一这已领域.所以去学了学.网上教程特别多.繁杂. 说的清楚的特别少. 我是这么跑通的,: 1.在官网下载代码地址为:http ...

  5. python入门(续)

    类和方法 创建类 class A(object): def add(self, a,b ): return a+b count = A() print(count.add(3,5)) 初始化工作 cl ...

  6. linux-课题练习1

    1.创建组testgroup: 2.创建用户a2012,先采用默认设置创建,然后使该用户加入testgroup组. 3.创建用户a2013,其用户主目录为/tmp/a2013,其主组为testgrou ...

  7. MFC接收ShellExecute多个参数

    在应用程序开发过程中,我们经常需要带参数启动另一个执行程序,如何传递多个参数,如何解析多个参数呢?   传参数 传递参数可使用ShellExecute函数,示例如下: ShellExecute(NUL ...

  8. TRANSLATE(转换大/小写并替换字符)

    可以将字母 转换大/小 写或使用替 换规则. 要转换大/小 写,请使用 TRANSLATE 语句,用法 如下: 语法 TRANSLATE <c> TO UPPER CASE. TRANSL ...

  9. Java线程:概念与使用

    Java线程大总结 原文章地址:一篇很老的专栏,但是现在看起来也感觉深受启发,知识点很多,很多线程特点我没有看,尴尬.但是还是整理了一下排版,转载一下. 操作系统中线程和进程的概念 在现代操作系统中, ...

  10. 基于Ubuntu Server 16.04 LTS版本安装和部署Django之(一):安装Python3-pip和Django

    近期开始学习基于Linux平台的Django开发,想配置一台可以发布的服务器,经过近一个月的努力,终于掌握了基于Apache和mod-wsgi插件的部署模式,自己也写了一个教程,一是让自己有个记录,二 ...