这是继前两篇死锁文章之后的第三篇文章,依然是使用windbg进行调试。欢迎各位阅读,希望对你有所帮助。我列出了前两篇文章的链接,方便各位查看。
[原]调试DLL卸载时的死锁
前言
这是几年前在项目中遇到的一个死锁问题。本文假设小伙伴们知道一些基本概念。比如,什么是.dump文件(转储文件,进程在某一时刻的快照),什么是windbg(windows下的调试利器),windbg的基本命令,调用栈,调用约定,等等。
背景介绍
我们的程序会把dll注入到其它进程,然后后调用SetWinEventHook安装进程内钩子。然后专门开启一个线程来分发监听到的事件信息,并在该线程内会获取IAccessible接口并使用该接口获取感兴趣的属性(e.g. 按钮的标题,按钮的位置等等)。运行我们的程序后,再启动excel,有时候会导致excel卡死,本文分析的.dump文件就是其中一次卡死时抓取的。背景介绍完毕,下面开始分析.dump。
问题分析
首先,我们用windbg打开.dump文件,因为当时没截图,这里就不放图了。
因为是UI线程卡死,而且一般情况下,第一个线程就是UI线程,所以我们使用~0s命令来切换到UI线程。

然后用kv命令列出调用栈,因为比较长,这里只用了kv10,列出前16个栈帧(windbg默认是16进制)。

我们发现0号线程进入关键段时卡住了,在等待值为0x544的句柄,猜测应该是关键段内部的LockSemaphore(类型为的event,可以通过!handle 0x544来验证,之前忘记截图了,这里就不放图了),我们可以用命令!cs 76b18770来观察关键段76b18770的内容。

通过OwningThread字段我们可以知道关键段76b18770被线程0x000001ac占用着,而0号线程的线程id是0x00000de4。
我们可以通过? $tid来观察当前线程的id(因为我们之前用~0s切换到0号线程了,所以观察到的是0号线程对应的id)
我们切换到线程id为0x000001ac的线程看看, 可以使用~~[native_thread_id]s来根据线程id切换线程。输入~~[0x000001ac]s;kv

从上图可知,线程0x000001ac正是我们新建的线程。从调用栈可知,本线程正在取name属性。但是因为某些原因触发了异常!frame 2表示正在调用SuspendThread挂起句柄为fffffffe的线程!也就是当前线程!!!我们可以反汇编KERNELBASE!GetCurrentThread来验证。输入uf KERNELBASE!GetCurrentThread

小结
0号线程尝试进入关键段76b18770,而关键段76b18770正在被线程id为0x000001ac的线程占用着,该线程又由于某些异常将自己挂起了!于是????死锁了!!!
提示
windbg有一个很有用的命令可以帮助我们快速找到死锁的关键段!
输入!cs -l

我们也可以从上图中看到线程0x000001ac拥有着关键段76b18770。
后记
windbg可谓是windows下调试的神兵利器。熟练掌握windbg是每一个优秀的windows开发人员必备的技能!而掌握windbg中的各种命令是掌握windbg的基础。今天的命令你掌握了吗?
参考资料
《格蠹汇编》
windbg 帮助文件

198

被折叠的 条评论
为什么被折叠?



