说明
使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记。本篇对 VLD 1.0 源码做内存泄漏检测的思路进行剖析。同系列文章目录可见 《内存泄漏检测工具》目录
1. 源码获取
version 1.0
及之前版本都使用旧的检测思路,可以在网站 CodeProject-Visual-Leak-Detector 中下载 version 1.0
的源码(国内网络资源:百度网盘-vld-1.0 源码包),同时在该网站中可以看到库作者 Dan Moulding
对旧检测原理的介绍。这个网站中有下图这段文字,但经过我一番查找,还是未找到 Dan Moulding
对后续新检测原理的介绍文章,本篇文章主要对 version 1.0
的源码进行剖析。
version 1.0
的源码算上注释一共不到 3000
行,而且代码注释写得很详细,推荐有兴趣的仔细阅读源码。以下资料可能对理解其检测原理有帮助:
- CodeProject-Visual-Leak-Detector。
- 博客园-vs 2010 下使用 VLD 工具。
- 博客园-关于内存泄漏。
- Github-dbgint.h。这个文件的第 310~335 行有结构体
_CrtMemBlockHeader
与pHdr()
的定义。
2. 源码文件概览
version 1.0
源码包中一共有 11
个文件,目录结构如下:
vld-10-src
CHANGES.txt
COPYING.txt
README.html
vld.cpp
vld.dsp
vld.h
vldapi.cpp
vldapi.h
vldint.h
vldutil.cpp
vldutil.h
其中 3
个 .cpp
文件,4
个 .h
文件,2
个 .txt
文件,1
个 .dsp
文件,1
个 .html
文件,各文件用途简述如下:
-
文件
README.html
为网页版的帮助文档,里面介绍了VLD
的功能、使用方法、配置选项、编译方法、功能限制等。从这个帮助文档中可以得知:这个版本的VLD
只能检测由new
或malloc
导致的内存泄漏,无法检测COM
泄漏、进程外的泄漏、与CRT
堆无关的泄漏;当需要检测多个DLL
库时,则要在加载的第一个模块中包含vld.h
头文件(若第一个加载的模块是个某个DLL
,则在该DLL
的主源文件中包含vld.h
,若第一个加载的是主exe
,则在main
函数所在文件中包含vld.h
)。 -
文件
CHANGES.txt
为版本迭代日志,记录了各版本的更新概要; -
文件
COPYING.txt
为LGPL 2.1
开源协议; -
文件
vld.dsp
为Visual C++
的项目文件,全称Microsoft Developer Studio Project File
; -
文件
vldapi.h
为使用VLD
库时需包含的头文件之一,里面声明了两个接口:VLDEnable()
与VLDDisable()
; -
文件
vldapi.cpp
里面是接口VLDEnable()
与VLDDisable()
的函数定义; -
文件
vldint.h
里面定义了dbghelp.dll
中一些函数的别名,并声明了VisualLeakDetector
类; -
文件
vldutil.h
里面定义了一些VLD
内部使用的宏,重载了内部的new/delete
运算符,并声明了CallStack
类与BlockMap
类,这两个类是VLD
自定义的数据结构,用来存储泄漏信息,CallStack
类似于STL vector
,BlockMap
类似于STL map
; -
文件
vldutil.cpp
为CallStack
与BlockMap
的类方法实现; -
文件
vld.h
为使用VLD
库时需包含的头文件之一,里面是一些配置选项的宏定义,用户可使用这些宏来定制VLD
的功能。特别地,这个文件里有以下一行代码,用来强制引用VLD
库中的全局对象visualleakdetector
,使其链接到当前程序(资料参考 MSDN-pragma-comment、MSDN-Linker-options、MSDN-/INCLUDE)。// Force a symbolic reference to the global VisualLeakDetector class object from // the library. This enusres that the object is linked with the program, even // though nobody directly references it outside of the library. #pragma comment(linker, "/include:?visualleakdetector@@3VVisualLeakDetector@@A")
-
文件
vld.cpp
为VisualLeakDetector
的类方法实现,主要功能的代码都在这个文件里;
3. 源码剖析
3.1 注册自定义 AllocHook 函数
使用 #pragma init_seg (compiler)
指令构造一个全局对象 visualleakdetector
,来确保这个对象的构造函数最先被调用(详见 vld.cpp
第 49~55 行)。
// The one and only VisualLeakDetector object instance. This is placed in the
// "compiler" initialization area, so that it gets constructed during C runtime
// initialization and before any user global objects are constructed. Also,
// disable the warning about us using the "compiler" initialization area.
#pragma warning (disable:4074)
#pragma init_seg (compiler)
VisualLeakDetector visualleakdetector;
在全局对象 visualleakdetector
的构造函数中调用 _CrtSetAllocHook 接口注册自定义 AllocHook
函数,使程序能捕捉之后的内存操作(内存分配/内存释放)事件(详见 vld.cpp
第 57~95 行)。
// Constructor - Dynamically links with the Debug Help Library and installs the
// allocation hook function so that the C runtime's debug heap manager will
// call the hook function for every heap request.
//
VisualLeakDetector::VisualLeakDetector ()
{
...
if (m_tlsindex == TLS_OUT_OF_INDEXES) {
report("ERROR: Visual Leak Detector: C