捉虫记2:windows程序句柄泄露的上下文环境
作为程序员,开发程序是基本功,而调试程序也是必不可少的技能之一。软件在主体功能开发完成后会经历各个阶段的测试,才会被发布。在测试过程中,出现较多的可能就是内存泄漏,句柄泄漏,异常崩溃等属于非功能型的软件Bug。而Windows作为一个相当成熟的平台,对于软件的调试也支持很到位。今天想要记录的是这次调查的一个模块的句柄泄漏问题。
关于句柄泄漏的文章网上很多,很多关于调试的书籍中也有说明,而且有些也比较详细。之前也解决过这类的问题,所以毫不在意。先介绍一下基本情况:工作机是Windows 7 64bit 专业版SP1,Windbg使用的是32bit的版本,出问题模块是32bit,依赖也多个外部模块,均为32bit,且自身包含了dbghelp.dll(后面介绍为什么单独说这个)。
照着Bug的描述,开始操作前搞上Windgb,设置好pdb,!htrace开启。操作了一番,在任务管理器中,发现被调试模块的句柄数蹭蹭蹭往上涨,停止操作后也没见有回落的迹象。见此情形,只好断入Windbg看看情况,看到!htrace -diff输入后显示的结果立马傻眼了。全都是虚地址,没有任何堆栈信息,立马想到是不是pdb不对,lmvm一看,显示正确。但是k显示的堆栈确实是有被调试模块的堆栈。一时想不到问题出在哪,那么就自己写个demo,看下是不是一样,写完demo挂上windbg,情况居然一样!这是感觉就有点诡异,问题应该不是出在被调试程序上,就度娘了一把,无任何发现。倒是在高端调试网站上有人发了个帖子,状况和我一样,但是没人回答怎么解决,无果。翻阅书籍,看到的都是千篇一律的,对于上述碰到的问题,无任何提及,无果。偶然在网上找到个工具http://pan.baidu.com/s/1jGIopqm,可以检测内存泄漏、句柄泄漏。将上面写的demo用上后,确实如实反映出了事件的泄漏点,感觉不错。然后就把出问题的模块用上。Inject的时候爆出一个错误,说被调试进程已经加载过dbghelp.dll。也就是上面特别提及的那点,无果。
想来想去,后来干脆就上了个64bit的Windbg,在调试32bit的demo时,!htrace -diff居然显示了几行堆栈,切换到x86模式下显示,确实显示了CreateEvent字眼,瞬间好像明白了什么。把demo编译条件切换到64bit,用64bit windbg再试,堆栈全部显示。然后就将泄漏的模块装在32bit机子上,用32bit windbg一看。未关闭句柄的堆栈全有了。至此水落石出。
为什么在64bit机器上,32bit的windbg调试32bit的进程,一般的函数调用,都能够显示堆栈,而对于差异的句柄堆栈,却不显示,没有答案。经过这一次发现,只关注问题的本身很重要,但有时往往会被一切外部的因素干扰到我们对事物的判断,这时候就应该站在一个高的高度,看待这问题,那么一切都明了了。