最近项目里遇到一个崩溃,不定期出现,很是头疼!今晚终于忍无可忍,下决心要干掉它!(于是用凉水洗了把脸,开始分析dump)希望凌晨的这篇总结对有相似经历的朋友有所启发!(看之前相关的几个dump可以猜到是有接口未释放的问题,但没有确认到底是哪个接口,本篇总结的主要目的是记录找到这个接口的过程)
用windbg打开dump文件,显示如下
很明显是访问违例!从红框里的内容可知要取地址5bbc97f8的值,然后call之。但是5bbc97f8已经无效了!用!address 5bbc97f8来看下该地址的信息
可知该地址确实已经不在内存中了!对应的模块已经被卸载了!
至此,可以知道崩溃的原因了:模块被卸载后,还要执行其中的代码!下面让我们继续分析,为什么模块卸载后还会执行其中的代码!
先用.ecxr切到出问题的线程,然后用kpn看下调用栈,如下图
可以知道是某个线程在调用CoUninitialize,执行com的线程清理工作!frame 0的ole32!CStdMarshal::DisconnectSrvIPIDs调用导致了崩溃!看名字是要断开链接相关的!
用ub 76f8bbe7 查看下崩溃前的几条指令
红框内的指令非常像COM函数(调用约定是stdcall) eax是com指针 ecx是虚表指针
dword ptr[ecx+8]是虚表中的第三项(32位下指针占4字节),回想IUnkown的前三个函数分别是QueryInterface AddRefRelease,可以基本确认这里是在调用Release(),跟上下文也很搭!我们的猜测99%是正确的!
我们先小小总结下:来龙去脉基本是这样的,我们使用了一个com接口,但是我们并没有释放,在调用CoUninitialize的时候,com做线程清理相关工作,当清理到我们的接口的时候,因为我们的dll已经被卸载了,导致了访问违例!下面我们的任务是找出到底是哪个接口没释放,然后对照代码验证我们的猜测是否正确!
先用lm a 5bbc97f8确认下该地址属于哪个模块
用命令.reload /f AccIME.dll=5bbb0000,5bbd8000-5bbb0000加载已经被卸载的模块及其对应的符号信息,并用lm vm AccIME确认
注意:如果遇到如下错误,说明我们符号加载的有问题,可以打开!sym noisy选项后看输出日志来排查,用完后可以用!sym quiet恢复,具体使用方法可通过.hh !sym查看帮助文档
接下来我们用ln 5bbc97f8查一下该地址附近对应的函数名
至此,我们可以看到是CCfgDpyScheme类相关的,可以把重点放到对此类的引用计数的使用上了!
本篇总结暂时写到这,还需要弄明白几个问题:
1. 涉及引用计数的相关代码是否有问题
2. 为什么有时候会崩溃,有时候不崩溃
有结果后会再发一篇总结,敬请期待!