dsChecker通过修改程序的DLLImportTable让table中的函数地址指向自己的地址,以达到截获的目的。关于如何拦截Wi
dows的系统函数,《程序员》杂志2002年8期,《API钩子揭密(下)》,对修改导入地址表做了概要的描述。我就不再赘述。r
r
r
r
截获住这些分配和释放函数,Bou
dsChecker就能记录被分配的内存或资源的生命周期。接下来的问题是如何与源代码相关,也就是说当Bou
dsChecker检测到内存泄漏,它如何报告这块内存块是哪段代码分配的。答案是调试信息(DebugI
formatio
)。当我们编译一个Debug版的程序时,编译器会把源代码和二进制代码之间的对应关系记录下来,放到一个单独的文件里pdb或者直接连结进目标程蛑小S辛苏庑┬畔魇云鞑拍芡瓿啥系闵柚茫ゲ街葱校榭幢淞康裙δ堋SPANBou
dsChecker支持多种调试信息格式,它通过直接读取调试信息就能得到分配某块内存的源代码在哪个文件,哪一行上。使用CodeI
jectio
和DebugI
formatio
,使Bou
dsChecker不但能记录呼叫分配函数的源代码的位置,而且还能记录分配时的CallStack,以及CallStack上的函数的源代码位置。这在使用像MFC这样的类库时非常有用,以下我用一个例子来说明:r
r
r
r
voidShowXItemMe
ur
r
r
r
…r
r
CMe
ume
ur
r
me
uCreatePopupMe
ur
r
addme
uitemsr
r
me
uTrackPropupMe
ur
r
…r
r
r
r
r
r
voidShowYItemMe
ur
r
r
r
…r
r
CMe
ume
ur
r
me
uCreatePopupMe
ur
r
addme
uitemsr
r
me
uTrackPropupMe
ur
r
me
uDetachthiswillcauseHMENUleakr
r
…r
r
r
r
r
r
BOOLCMe
uCreatePopupMe
ur
r
r
r
…r
r
hMe
uCreatePopupMe
ur
r
…r
r
r
r
r
r
r
当调用ShowYItemMe
u时,我们故意造成HMENU的泄漏。但是,对于Bou
dsChecker来说被泄漏的HMENU是在classCMe
uCreatePopupMe
u中分配的。假设的你的程序有许多地方使用了CMe
u的CreatePopupMe
u函数,如皇歉嫠吣阈孤┦怯SPANCMe
uCreatePopupMe
u造成的,你依然无法确认问题的根结到底在哪里,在ShowXItemMe
u中还是在ShowYItemMe
u中,或者还有其它的地方也使用了CreatePopupMe
u?有了CallStack的信息,问题就容易了。Bou
dsChecker会如下报告泄漏的HMENU的信息:r
r
r
r
Fu
ctio
r
Filer
Li
er
r
CMe
uCreatePopupMe
ur
E8168vc98mfcmfci
cludeafxwi
1i
lr
1009r
r
ShowYItemMe
ur
Etestmemleakmytestcppr
100r
r
这里省略了其他的函数调用r
r
r
r
r
r
r
r
如此,我们很容易找到发生问题的函数是ShowYItemMe
u。当使用MFC之类的类库编程时,大部分的API调用都被封装在类库的class里,有了CallStack信息,我们就可以非常容易的追踪到真正发生泄漏的代码。r
r
r
r
记录CallStack信息会使程序的运行变得非常慢,因此默认情况下Bou
dsChecker不会记录CallStack信息。可r