缘起
最近,接连在项目中遇到了两个界面无响应的问题。都只发生在客户特定机器上,不方便直接调试,只能抓取 dump
进行事后分析了。
抓取 dump
远程连上可以重现问题的机器,使用 process explorer
初步观察卡死的进程,发现 CPU
占用率很低,经过一段时间的观察,基本确定是一个死锁问题。在卡死的进程上右键,保存完整转储,压缩,发回本地进行分析。
使用 windbg 进行分析
双击抓取的 dump
文件,因为之前已经执行过 windbg.exe -IA
,所以默认会通过 windbg
打开 dump
文件。先使用 ~*kvn
粗略浏览一下每个线程的调用栈(因为比较长,这里就不截图了)。经过观察,很快锁定了两个值得进一步查看的线程:一个是主线程(界面线程),因为是界面无响应,肯定要关注界面线程。另外一个是 7
号工作线程。分别看一下这两个线程的调用栈。
主线程的调用栈如下图所示:
注意上图红色高亮部分,主线程通过 SleepConditionVariableCS()
进入等待。
看完主线程,再看 7
号工作线程的调用栈,如下图所示:
7
号线程对 SendMessage()
的调用非常值得怀疑。
猜测整个流程是这样的:主线程不知由于什么原因进入等待状态,而工作线程由于各种各样的原因也进入了等待状态。其中 7
号线程最明显,因为它正在发消息,而主线程此时是无论如何也不会响应这个消息的。
于是,典型的死锁再一次发生了。
加载 AssemblyDesign_Tools.dll
的符号后,查看对应的源码,消除对 SendMessage()
的调用,问题解决!so easy!
说明:工作线程中并没有直接调用
SendMessage()
,而是调用了操作界面的相关API
,间接调用了SendMessage()
给界面线程发消息。
死锁的问题解决了,但是为什么向主线程发个消息就死锁了呢?秉着打破砂锅问到底的原则,我又开始折腾了。下面的内容适合喜欢调试逆向的极客阅读。
深入调查
最开始的思路是:查看主线程在等待的条件变量,然后再调查哪个工作线程会唤醒这个条件变量。奈何 64
位下,前四个参数通过寄存器 RCX, RDX, r8, r9
进行传递,如果这些寄存器没有在栈上存储一份的话&