Visual Leak Detector原理剖析
认识VLD
VLD(Visual Leak Detector)是一款用于Visual C++的开源内存泄漏检测工具,我们只需要在被检测内存泄漏的工程代码里#include “vld.h”就可以开启内存泄漏检测功能。当我们使用Visual Studio debugger来调试我们的进程时,VLD可以在程序退出时很直观地将泄漏的内存地址、堆栈、大小、内容输出到Visual Studio的Output窗口,此时我们只需要直接双击调用堆栈就可以跳转到对应的代码行,从而直接明了的知道哪里分配了的内存没有被释放,如下图:

本文要剖析的VLD版本是V2.3,其源码可以在http://vld.codeplex.com/下载到。
VLD原理概述
从本质上来说,VLD是通过监控所有内存操作(分配和释放)来检测内存泄漏的,而监控内存操作的方式也有很多种,比如Hook所有内存操作函数到自定义的函数中,或者通过修改IAT(Import Address Table导入地址表)来将原本要调用的内存操作函数修改为VLD自已的函数。这里VLD 的方法是后者。
VLD原理剖析
从#include “vld.h”开始看去
1. 首先我们在vld.h文件里可以看到#if defined _DEBUG || defined VLD_FORCE_ENABLE,这里表示的意思是Debug版本是默认开启监控的,但如果使用的是Release版本的话就需要在工程设置里加上VLD_FORCE_ENABLE预定义宏内存监控才会生效,这点也是需要提醒各位看官的。
2. 再者我们看到可以看到#pragma comment(lib, "vld.lib")这行代码。其实vld工程本身是一个dll工程,但dll工程编译时同时也会生成一个lib,通过这行代码我们的程序就不需要手动调用LoadLibrary来加载dll了。
3. 再者我们可以看到这样一段代码:

大概的意思就是导出一个对象(这个对象的名字叫g_vld,它是vld.cpp里的一个全局变量)为我们的程序所看到,从而保证vld.dll会被加载,不然编译器可能会认为vld.dll里我们的代码并没有依赖vld从而给优化掉了。这样,当vld.dll被加载时,这个对象就会被初始化,从而修改各dll的导入地址表来做监控所有内存操作。
从g_vld对象看VLD如何监控所有内存操作
1. 我们在vld.cpp里可以看到这个对象的定义,它是类VisualLeakDetector的实例,当此对象被初始化时会调用类的构造函数,接下来我们看看构造函数里做了什么事。
2. 首先,我们看看下面这段代码:

这里枚举了所有已加载的模块,然后再对枚举到的模块调用attachToLoadedModules,接下来我们看看attachToLoadedModules做了什么事情。
3. 在attachToLoadedModules函数里,它会枚举所有的模块,并调用下面这行代码:

这个函数的主要工作就是修改moduleBase所指示的模块的导入地址表,下面我看看传入参数m_patchTable是什么。
4. 可以看到m_patchTable是一个moduleentry_t数组,moduleentry_t的定义(在utility.h文件里)如下:

它的第一个成员exportModuleName表示的是一个dll的名字,第二个成员moduleBase表示的是该dll的基地址,第三个参数patchTable是一个结构体的指针,我们再看看patchentry_t的定义(在utility.h文件里):

其中importName是api的名字,这些API都内存操作相关的API,original表示的是该API的函数地址,replacement是VLD内部一个函数的地址,它是用来替换原来的函数地址的。
5. 接下来看看m_patchTable的具体内容(部分)吧:

再取其中的m_kernel32Patch看看其具体内容:

6. 通过代码可以发现PatchModule函数拿到上面的内存后,会对m_patchTable数组里的每一个元素调用PatchImport函数,而PatchImport就是真正修改导入地址表的地方。
看VLD如何管理监控到的内存
1. 所有内存分配最终都会调用kernel32的HeapAlloc以及HeapReAlloc函数,而前文的介绍中已经提到这两个函数会被VLD内部函数通过修改导入地址表而替换。各位看官看到这里可能会问:“既然所有内存分配最终都会走到这里,那为什么不直接监控这几个函数呢?为什么还要监控那么多其他函数(malloc,new,realloc,calloc等等)?”我的理解是:为了获取调用malloc,new,realloc,calloc这些函数时的堆栈。虽然从HeapAlloc开始回溯堆栈也没有什么问题,但VLD这不是为了美观么,不想有多余的干扰信息。
2. VLD监控到分配的内存后会保存到一个map结构中:

这里以堆句柄为first key,second是一个结构体heapinfo_t,定义如下:

这里的成员blockMap又是一个map:

BlockMap以分配的内存地址为first key,然后second又是一个结构体,定义如下:

这里保存了所监控到的内存分配堆栈、分配序号、大小等信息。
3. 有了这个map结构后,当有内存被分配时,就会向这个map插入元素;而当有内存被释放时,就会从这个map中删除对应的元素。
4. 最后,当程序退出时,g_vld对象会被析构,此时map中的所有元素就是没有被释放的内存,也就是泄漏的内存。
总结
以上就是VLD的基本原理了,其实在VLD早前的版本中它并不是用的这种修改导入地址表的机制,这里就不再赘述了。总的来说,VLD还是一款简单易用的内存泄漏检测工具,但是它也有自已的缺陷:需要修改源程序的代码。这就使得需要有代码的情况下才能够检查内存泄漏,但对于测试人员来说,有时我们拿到的只是编译好的二进制,情况好点的话会有PDB,此时如何去检查内存泄漏也是一个值得研究的方向。
Visual Leak Detector原理剖析的更多相关文章
- 使用Visual Leak Detector检测内存泄漏[转]
1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...
- vld(Visual Leak Detector) 内存泄露检测工具
初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复 杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题.内存 ...
- 使用Visual Leak Detector for Visual C++ 捕捉内存泄露
什么是内存泄漏? 内存泄漏(memory leak),指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段 ...
- Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector
那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能,我们可以使用第三方工具Visual Leak Detector(以下简 ...
- Visual Leak Detector 2.2.3 Visual C++内存检测工具
Visual Leak Detector是一款免费的.健全的.开源的Visual C++内存泄露检测系统.相比Visual C++自带的内存检测机制,Visual Leak Detector可以显 ...
- Cocos性能优化工具的开发介绍Visual Studio内存泄漏检测工具——Visual Leak Detector
然后,Windows下有什么好的内存泄漏检測工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检測功能.我们能够使用第三方工具Visual Leak Detector(下面简 ...
- VisualStudio 怎么使用Visual Leak Detector
VisualStudio 怎么使用Visual Leak Detector 那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测 ...
- Visual Leak Detector简明使用教程
Visual Leak Detector是一款内存泄漏检测软件,主要的作用就是检测可能或者是存在内存泄露的地方,具体的功能的话,可以百度下,今天主要简单介绍下怎么使用 首先下载Visual Leak ...
- VS2017 编译 Visual Leak Detector + VLD 使用示例
起因 一个Qt5+VS2017的工程,需要进行串口操作,在自动时发现一段时间软件崩溃了,没有保存log,在 debug 的时候发现每运行一次应用占据的内存就多一点,后来意识到是内存泄漏了.这个真是头疼 ...
随机推荐
- 【SQLSERVER】动态游标的实现
方法1: CREATE TABLE #tabTmp(id int) INSERT #tabTmp EXECUTE('SELECT id FROM '+@Table ...
- 性能分析_linux服务器CPU_中断
中断 1. 指标范围 1.1 Interrupt rate 应该与cpu利用率结合分析,如果cpu利用率在合理范围内,大量的中断也是可以接受的.一个巨大的中断值,同时伴随着缓慢的系统性能表现,指示 ...
- Ubuntu安装使用中的一些注意事项
在win7上安装VMware workstations10.0 ,在VMware workstations10.0上安装Ubuntu14.04 64位时,关于网络的连接注意: win7 网络连接里上的 ...
- poj 1523 SPF(双连通分量割点模板)
题目链接:http://poj.org/problem?id=1523 题意:给出无向图的若干条边,求割点以及各个删掉其中一个割点后将图分为几块. 题目分析:割点用tarjan算法求出来,对于每个割点 ...
- 12:打印1到最大的n位数
面试题12:打印1到最大的n位数 剑指offer题目12,题目如下 输入数字n,按顺序打印出1到最大的n位十进制数,比如输入3,则打印出1,2,3一直到最大的三位数999 方法一 和面试题11< ...
- SpringBoot(十)_springboot集成Redis
Redis 介绍 Redis是一款开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存也可持久化的日志型.Key-Value高性能数据库. 数据模型 Redis 数据模型不仅与关系数据 ...
- MT【148】凸数列
(2018浙江省赛13题) 设实数$x_1,x_2,\cdots,x_{2018}$满足$x_{n+1}^2\le x_nx_{n+2},(n=1,2,\cdots,2016)$和$\prod\lim ...
- 【bzoj1833】 ZJOI2010—count 数字计数
http://www.lydsy.com/JudgeOnline/problem.php?id=1833 (题目链接) 题意 求在${[a,b]}$范围内整数中,每个数码出现的次数. Solution ...
- 【poj3623】 Best Cow Line, Gold
http://poj.org/problem?id=3623 (题目链接) 题意 给出一个字符串,每次可以取首或尾接到一个新的字符串后面,求构出的字典序最小的新字符串. Solution 首先可以发现 ...
- digitalworld.local: MERCY靶机入侵
0x01 前言 MERCY是一个致力于PWK课程安全的靶机系统.MERCY是一款游戏名称,与易受攻击的靶机名称无关.本次实验是攻击目标靶机获取root权限并读系统目录中的proof.txt信息 靶机的 ...