目录

Valgrind的简介

Valgrind的使用方法

1. 使用步骤

1.1 编译程序

1.2 用Valgrind运行程序

2. 错误类型

2.1 illegal read 非法读 / illegal write 非法写

2.2 Use of uninitialised values 使用未初始化的值

2.3 Use of uninitialised or unaddressable values in system calls 系统调用时使用未初始化的值或不能访问的地址

2.4 illegal fress 非法释放

2.5 When a heap block is freed with an inappropriate deallocation function 不匹配的释放

2.6 Overlapping source and destination blocks 移动/复制缓存数据时,源与目标重叠

2.7 Fishy argument values 函数调用的参数可能不合法

2.8 Memory leak detection 内存泄露

3. 关于内存泄露

4. memcheck的实现细节

4.1 V (Valid-value) bit

4.2 A (Valid-address) bit

相关链接

参考资料


Valgrind的简介


Valgrind是用于构建动态分析工具的仪器框架。有Valgrind工具可以自动检测许多内存管理和线程错误,并详细描述程序。您也可以使用Valgrind构建新工具。

Valgrind发行版目前包含七个生产质量工具:一个内存错误检测器,两个线程错误检测器,一个缓存和分支预测探查器,一个生成调用图的缓存和分支预测探查器以及两个不同的堆探查器。它还包括一个实验性的SimPoint基本块矢量生成器。它可在以下平台上运行:X86 / Linux,AMD64 / Linux,ARM / Linux,ARM64 / Linux,PPC32 / Linux,PPC64 / Linux,PPC64LE / Linux,S390X / Linux,MIPS32 / Linux,MIPS64 / Linux,X86 / Solaris ,AMD64 / Solaris,ARM / Android(2.3.x和更高版本),ARM64 / Android,X86 / Android(4.0和更高版本),MIPS32 / Android,X86 / Darwin和AMD64 / Darwin(Mac OS X 10.12)。

Valgrind是开放源代码 / 免费软件,可根据 GNU通用公共许可版本2免费获得。

Valgrind的使用方法


https://www.jianshu.com/p/78adaba826c3

Valgrind是一组调试(debugging)和剖析(profiling)工具的集合。memcheck是其中应用最广泛的一个,它检查内存有关的问题,包括诸如内存访问越界、内存泄露等。

1. 使用步骤

1.1 编译程序

  • -g:使用这个选项产生符号信息。这样Valgrind产生的报告中会显示的代码位置是源代码的位置,否则Valgrind只能猜函数名。
  • -fno-inline:不使用这个选项,Valgrind报告的调用栈可能让人迷惑。如果是编译debug版本,这个选项是默认的
  • -O1。使用这个选项在速度与准确性之间达到平衡。如果速度可容忍,最好使用-O0。
  • -Wall。使用这个选项时,gcc能解决一些memcheck不能检测到的问题。

1.2 用Valgrind运行程序

valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full \
--show-leak-kinds=all ./your_app arg1 arg2
  • --log-file 报告文件名。如果没有指定,输出到stderr。
  • --tool=memcheck 指定Valgrind使用的工具。Valgrind是一个工具集,包括Memcheck、Cachegrind、Callgrind等多个工具。memcheck是缺省项。
  • --leak-check 指定如何报告内存泄漏(memcheck能检查多种内存使用错误,内存泄漏是其中常见的一种),可选值有:
    • no 不报告
    • summary 显示简要信息,有多少个内存泄漏。summary是缺省值。
    • yes 和 full 显示每个泄漏的内存在哪里分配。
  • show-leak-kinds 指定显示内存泄漏的类型的组合。类型包括definite, indirect, possible,reachable。也可以指定all或none。缺省值是definite,possible。

2. 错误类型

2.1 illegal read 非法读 / illegal write 非法写

如下代码分配了长度为1024的缓存buf,然后在buf+1020的位置写入一个8字节的uint64_t,并打印它。这个uint64_t已经超出buf的界限。

char* buf = (char*) malloc (1024);
uint64_t* bigNum = (uint64_t*)(buf + 1020);
*bigNum = 0x12345678AABBCCDD;
printf ("bigNum is %llu\n", *bigNum);
free(buf);

报告指出了非法写的位置main.cpp:12和非法读的位置main.cpp:13。

==17387== Invalid write of size 8
==17387==    at 0x400849: main (main.cpp:12)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)
==17387==
==17387== Invalid read of size 8
==17387==    at 0x400850: main (main.cpp:13)
==17387==  Address 0x51f343c is 1,020 bytes inside a block of size 1,024 alloc'd
==17387==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17387==    by 0x400828: main (main.cpp:10)

2.2 Use of uninitialised values 使用未初始化的值

如下代码打印了一个未初始化的值unused。

10     int unused;
11     printf ("unused=%d", unused);

报告指出了使用unused的位置main.cpp:11。

==17418== Use of uninitialised value of size 8
==17418==    at 0x4E7A39B: _itoa_word (_itoa.c:195)
==17418==    by 0x4E7C3E7: vfprintf (vfprintf.c:1596)
==17418==    by 0x4E85198: printf (printf.c:35)
==17418==    by 0x4007A2: main (main.cpp:11)

2.3 Use of uninitialised or unaddressable values in system calls 系统调用时使用未初始化的值或不能访问的地址

如下代码中arr没有初始化,就调用write()输出了。

10     char* arr = (char*)malloc(10);
11     write (1, arr, 10);

报告给出了系统调用write的位置main.cpp:11。

==17450== Syscall param write(buf) points to uninitialised byte(s)
==17450==    at 0x4F1AF10: __write_nocancel (syscall-template.S:82)
==17450==    by 0x400802: main (main.cpp:11)
==17450==  Address 0x51f3040 is 0 bytes inside a block of size 10 alloc'd
==17450==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17450==    by 0x4007E8: main (main.cpp:10)

2.4 illegal fress 非法释放

如下代码中,释放的地址arr+2不是分配时得到的地址arr。

10     char* arr = (char*)malloc(10);
11     free (arr+2);

报告中指出了释放时的位置main.cpp:11,也指出了分配时的位置main.cpp:10。

==3429== Invalid free() / delete / delete[] / realloc()
==3429==    at 0x4C2A82E: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007FC: main (main.cpp:11)
==3429==  Address 0x51f3042 is 2 bytes inside a block of size 10 alloc'd
==3429==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3429==    by 0x4007E8: main (main.cpp:10)

2.5 When a heap block is freed with an inappropriate deallocation function 不匹配的释放

如下代码中分配得到一个int数组d[],释放的时候却使用delete,而不是delete[]。

10     int* d = new int[5];
11     delete d;

报告中指出了数组释放的位置main.cpp:11,也指出了分配的位置main.cpp:10。

==3498== Mismatched free() / delete / delete []
==3498==    at 0x4C2A4BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400828: main (main.cpp:11)
==3498==  Address 0x5a2ac80 is 0 bytes inside a block of size 20 alloc'd
==3498==    at 0x4C2AC27: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3498==    by 0x400818: main (main.cpp:10)

2.6 Overlapping source and destination blocks 移动/复制缓存数据时,源与目标重叠

如下代码中memcpy复制的源和目标重叠。

10     char buf[256] = "";
11     memcpy (buf+10, buf, 100);

报告指出了复制的位置main.cpp:11.

==3530== Source and destination overlap in memcpy(0x7feffffea, 0x7feffffe0, 100)
==3530==    at 0x4C2CFA0: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3530==    by 0x40087E: main (main.cpp:11)

2.7 Fishy argument values 函数调用的参数可能不合法

下面的代码中,使用了负值-10调用malloc。

10     int sz = -10;
11     int* arr2 = (int*)malloc (sz);
12     free(arr2);

报告只是警告malloc()的参数不合法,但没有指出位置。(就算不用Valgrind运行程序,这个错误应该也能马上发现吧?)

==3583== Warning: silly arg (-10) to malloc()

2.8 Memory leak detection 内存泄露

以下代码分配了100字节没有释放。

10     int* arr = (int*)malloc (100);

报告依次列出了每一个泄露的内存分配的位置(这里是main.cpp:10),最后还给出了所有关于泄露的统计总结(LEAK_SUMMARY部分)。

==3869== HEAP SUMMARY:
==3869==     in use at exit: 100 bytes in 1 blocks
==3869==   total heap usage: 1 allocs, 0 frees, 100 bytes allocated
==3869==
==3869== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3869==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3869==    by 0x400798: main (main.cpp:10)
==3869==
==3869== LEAK SUMMARY:
==3869==    definitely lost: 100 bytes in 1 blocks
==3869==    indirectly lost: 0 bytes in 0 blocks
==3869==    possibly lost: 0 bytes in 0 blocks
==3869==    still reachable: 0 bytes in 0 blocks
==3869==    suppressed: 0 bytes in 0 blocks

3. 关于内存泄露

  • memcheck跟踪所有malloc()/new()分配的堆块,所以它能在程序退出时知道哪些块还没有释放。
  • 这里把程序能访问到的指针集合叫做root-set。memcheck根据从root-set能不能到达某个块,判断这个块是不是泄露了(reachable还是lost)。
    • 到达块有两种方式:一是start-pointer,它指向块的开始地址;二是interior-pointer,它指向块的中间某个位置。
    • 有start-pointer引用的缓存被认为是reachable。
    • 有interior-pointer引用的缓存则不一定了。
      • 它可能是分配之后的指针故意向后移动的结果。这时程序使用缓存开始的几个字节保存特别信息,而真正的数据在后面某个位置。比如指向C++的std::string内部数组的指针、指向new[]分配的C++对象数组的指针、多重继承的情况等。这时缓存是reachable的。
      • 但它也可能只是偶然指向缓存。这时缓存就是lost的。
      • 所以interior-pointer引用的缓存是possible lost。

根据从root-set到缓存是否需要跳转,memcheck 将分配的缓存的状态分为几种:

  • directly reachable: root-set中的指针指向缓存,不需跳转
  • indirectly reachable: root-set中的指针不直接指向缓存,需要跳转
  • directly lost:没有任何指针指向缓存
  • indirectly lost:有指针指向缓存,但从root-set中也无法到达这个指针

下图列出了缓存的各种情况。其中RRR是root-set中的节点,AAA和BBB是缓存。

     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)DLPointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointerLeak 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

下面是memcheck中LEAK SUMMARY部分中的项目:

  • Still reachable: 上图中(1)和(2)的情况,从root-set中通过start-pointer直接到达或跳转到达缓存。这一项是确定不是lost的,用户无需关系。
  • Definitely lost:(3)的情况。没有任何指针指向缓存。这一项确定是lost的,用户需解决。
  • Indirectly lost:(4)和(9)中BBB的情况。有start-pointer或interior-pointer指向缓存,但从root-set不能到达这些指针。这一项可以推迟解决,因为Indirectly lost一定与definitely lost对应,definitely lost解决了,indirectly lost也就变成reachable或者possible lost了。
  • Possibly lost: 有interior-pointer指向缓存,从root-set能到达这些指针。因为memcheck不能判断interior-pointer是否lost,所以需要用户排除。
    所以实际上只要关心definitely lost和possible lost就可以了。这也是memcheck的缺省值:
--show-leak-kinds=definite,possible
  • suppressed: 用户在suppressed file中指定不要报告的项目,不管这个缓存是前面的哪种情况。

4. memcheck的实现细节

4.1 V (Valid-value) bit

可以简单地认为memcheck实现了一个与真实CPU对等的CPU,这个CPU对于真实CPU上处理和存储的每一个bit,都有一个与这个bit关联的额外的bit。后一个bit叫做V bit,表示前一个bit中是否包含合法值。

进程的整个地址空间对应一个bitmap,如果CPU加载一个word大小的数据,它同时从bitmap中加载对应的32个V bit。如果U将这个word数据的全部或部分写到一个不同的地址,则同时将那个地址对应的32个V bit写回bitmap。

即使是CPU内部的bit也有一个相应的V bit。所有的CPU寄存器,不管是什么类型,都有各自的V bit。memcheck在bitmap大量使用压缩技术,以便紧凑地表达其中的V bit。

4.2 A (Valid-address) bit

内存中的每个byte都有一个valid-address(A) bit与它对应。与V bit不同,CPU中的byte没有A bit。A bit表示程序是否能合法读写相应的位置,A bit 不关心数据,V bit才关心。

程序读写内存时,memcheck检查与该位置相应的A bit,如果 A bit无效,则报告错误。

A bit设置的时机如下:

  • 程序启动时,所有全局数据的范围A bit设置有效
  • 调用malloc/new时,分配大小的区域,A bit设置有效,释放后立即设置无效
  • 栈指针SP移动时,A bit相应设置。因为栈向下增长,所以SP向下移动,A bit设置有效,SP向上移动,A bit设置无效。
  • 某些系统调用时,A bit相应设置。比如mmap()将文件映射到内存空间,所以A bit设置有效。

相关链接

GDB 常用法
GDB 调试Coredump问题
嵌入式开发中GDB调试Coredump问题
嵌入式开发中GDB串口远程调试
用backtrace()调试coredump问题
Valgrind memcheck 用法
Address Sanitizer 用法

参考资料

Memcheck: a memory error detector
http://valgrind.org/docs/manual/mc-manual.html#mc-manual.badfrees

valgrind 工具介绍和简单的使用
https://www.cnblogs.com/AndyStudy/p/6409287.html

关于gcc 2.96
https://www.jianshu.com/p/a317e8208f8e

作者:RonZheng2010
链接:https://www.jianshu.com/p/78adaba826c3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Valgrind动态分析工具相关推荐

  1. linux平台软件动态分析工具valgrind系列工具及其可视化

    linux平台软件动态分析工具valgrind系列工具 Memcheck–内存检查工具 Callgrind–函数调用分析工具 Cachegrind–缓存命中分析工具 Helgrind–线程分析工具 M ...

  2. linux平台下QtCreator中集成Valgrind系列工具

    linux平台下QtCreator中集成Valgrind系列工具 ###1.valgrind 安装 valgrind 安装 2.打开QtCreator >> Analyze 你就会发现 这 ...

  3. [系统安全] 四.OllyDbg动态分析工具基础用法及Crakeme逆向破解

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  4. 程序动态分析工具调研

    转自:http://blog.csdn.net/aneutron/article/details/49251921 目录(?)[+] 综述 本文是对程序动态工具的调研,通对各种动态分析工具的原理.功能 ...

  5. 病毒分析系列3 | 初步动态分析工具使用

    前言 接上篇.使用动态分析工具对病毒进行初步分析,可以确定和获取到病毒的相关操作 Process Explorer使用 在进程右键 →查看属性,可以获得关于该进程的信息,如TCP/IP流量信息:捕获异 ...

  6. linux静态反汇编工具,27款反汇编/调试器/静态和动态分析工具

    原标题:27款反汇编/调试器/静态和动态分析工具 本文从github上收集了27款实用的反汇编.调试器.静态和动态分析工具,文中的工具在<Awesome Windows Exploitation ...

  7. Inspeckage,安卓动态分析工具

    From:安卓分析工具 Inspeckage 介绍:http://xdxd.love/2016/08/09/安卓分析辅助工具Inspeckage介绍/ PJ 微信数据库 并查询数据上传服务器:http ...

  8. [网络安全自学篇] 六.OllyDbg动态分析工具基础用法及Crakeme逆向破解

    这是作者的系列网络安全自学教程,主要是关于网安工具和实践操作的在线笔记,特分享出来与博友共勉,希望您们喜欢,一起进步.上一篇文章分享了IDA Pro反汇编工具的基础用法,并简单讲解一个EXE逆向工程解 ...

  9. 【linux】Valgrind工具集详解(一):简介

    一.Valgrind概述 Valgrind是用于构建动态分析工具的仪器框架.它附带了一组工具,每个工具都执行某种调试,分析或类似任务,可帮助您改进程序.Valgrind的架构采用模块化设计,因此可以轻 ...

最新文章

  1. JS 总结之事件循环
  2. 如何创建和获取正则对象?
  3. Ubuntu下Nginx/PHP/MYSQL开发环境的配置方法
  4. 清华姚班的科研能力,能孵化出一家怎样的公司?
  5. php的v_PHPV是什么意思
  6. python+mysql:实现一千万条数据插入数据库
  7. 再上24天班,小长假就来了!
  8. 反思专注力:重视专注;转变认知;避开即时娱乐;控制专注;氛围想不专注都难
  9. Shell 字符串截取
  10. 一份关于机器学习中线性代数学习资源的汇总
  11. [120_移动开发Android]007_android开发之SAX操作XML文件
  12. 迷宫问题python实现
  13. Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Incorrect datetime value:
  14. google三件套是什么_什么是Google?
  15. 智能硬件再下一城:家居
  16. pandas——显示设置
  17. 百度AI开放平台[Python]
  18. 轩迅汇如何做好个人定位?定位越早,受益越多
  19. Linux——一文彻底了解进程id和线程id的关系(什么是pid、tgid、lwp、pthread_t)
  20. 换了新的JDK后,Eclipse打不开

热门文章

  1. python中assert是什么意思,python中assert断言的用法
  2. 【Vue-router中,require代替import解决vue项目首页加载时间过久】
  3. 用这款免费工具,即可解决 90% 的报表设计难题
  4. JAVA日期查询:季度、月份、星期等时间信息
  5. 2018-11-17 js的this引起的血案
  6. Vue-router学习(一)- 路由匹配
  7. 【HDU 4352】 XHXJ's LIS (数位DP+状态压缩+LIS)
  8. 2014.12.1---Thema:EchartsD3
  9. MySQL-事务的实现-redo
  10. 用Convert类实现数据类型转换