windbg解决线程死锁

               

1、打开将要被检测的软件A,同时打开windbg

2、windbg,File->attach to process ,附加到进程A

3、F5运行或DEBUG->GO

4、等待进程A进入死锁状态,然后DEBUG->Break,在下面的输入栏中输入   ~*kb,然后查看各线程信息,分析死锁原因


PS: 常用命令

0、!analyze -v 分析

1、清屏:.cls(注意前面有个 . 号)

2、d

命令显示esp寄存器指向的内存
如下


用dd命令直接指定054efc14地址

3、~ 用来切换目标线程:~显示线程信息;~0s把当前线程切换到0号线程

4、~* 列出当前进程中所有线程的详细信息

5、k 显示当前线程的堆栈

6、~*kb 显示当前进程所有线程的堆栈,同理~1kb显示一号线程堆栈

7、使用!locks命令列出当前被锁住的资源

8、

!cs Address 指定要显示的临界区地址。如果省略该参数,调试器显示当前进程中所有临界区。

!cs -s 如果可能的话,显示每个临界区的初始堆栈回溯。 !cs -l  仅显示锁定的临界区。



0614fd4c 7c92df5a 7c939b23 00000604 00000000 ntdll!KiFastSystemCallRet
0614fd50 7c939b23 00000604 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0614fdd8 7c921046 0012fe08 5f401b5f 0012fe08 ntdll!RtlpWaitForCriticalSection+0x132
这里的第一个是ebp,第二个是返回函数地址,接下来才是你的参数。
具体要看handle是多少,要看下比如 ntdll!NtWaitForSingleObject参数定义:

C/C++ code
?
1
2
3
4
5
NTSTATUS WINAPI NtWaitForSingleObject(
   _In_   HANDLE  Handle,
   _In_   BOOLEAN  Alertable,
   _In_  PLARGE_INTEGER Timeout
);

这三个 00000604 00000000 00000000分别对应上面的Handle,Alrtable,Timeout.
可见这里的handle值是0x00000604,其它函数参数也是以此类推,具体如果参数比较多时,可以直接打印那个esp来查看
堆栈里的内容。









9、

死锁问题,绝对是个令人发狂的问题,特别是工程比较大,引用开源库及第三方库比较多时。我最近就遇到这么一个死锁问题,工程是别人的,很多业务的代码都看不明白,这个时候出现死锁,让我郁闷了一个星期,终于在原来同事的帮助下解决了。

 

              长话短说,现在开始。当然默认你对线程同步有些了解,不然还是看一下核心编程比较好。

 还有一个事先要做的事情,就是打印出你工程里线程创建和注销的日志,把线程ID打印出来,方便定位到自己的代码。

              程序出现死锁的时候,用WINDBG的F6快捷键选中死锁的进程,然后使用命令“~*kv”,打印出所有的堆栈信息,根据堆栈信息找到发生死锁的线程。

如下图所示:

windbg的使用四(Windbg检查死锁 ) - 3792615 - 独一飞的博客

1.)  1号线程和2号线程堆栈里出现ntdll!RtlEnterCriticalSection,说明发生临界区死锁


2.)   WINDBG有个扩展命令叫!locks,可以显示所以在使用的锁,不过不知道为什么我的机器上用这个命令,总是提示NTDLL.DLL的PDB不对。还请高手指教,显示如下:

0:003> !locks
NTSDEXTS: Unable to resolve ntdll!RTL_CRITICAL_SECTION_DEBUG type
NTSDEXTS: Please check your symbols

3.)看来是不能指望这个扩展命令,还好同事给我另一个命令dt.使用dt RTL_CRITICAL_SECTION 004030f4;dt RTL_CRITICAL_SECTION 004030dc;

即可打出这两个锁的信息,004030f4和004030dc是ntdll!RtlEnterCriticalSection第一个参数。你可能会问为什么是第一个参数,这个跟ntdll!RtlEnterCriticalSection函数的定义有关。用同事的话说就是源码上第一个参数就是RTL_CRITICAL_SECTION结构。

显示如下:

0:003> dt RTL_CRITICAL_SECTION 004030f4;dt RTL_CRITICAL_SECTION 004030dc
CriticalSection!RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : 0x00146620 _RTL_CRITICAL_SECTION_DEBUG
   +0x004 LockCount        : 1
   +0x008 RecursionCount   : 1
   +0x00c OwningThread     : 0x00000a70 
   +0x010 LockSemaphore    : 0x000007bc 
   +0x014 SpinCount        : 0
CriticalSection!RTL_CRITICAL_SECTION
   +0x000 DebugInfo        : 0x001465f8 _RTL_CRITICAL_SECTION_DEBUG
   +0x004 LockCount        : 1
   +0x008 RecursionCount   : 1
   +0x00c OwningThread     : 0x000009c8 
   +0x010 LockSemaphore    : 0x000007b8 
   +0x014 SpinCount        : 0

WINDBG显示了两个锁的信息,我们比较关心哪几个线程发生了死锁,就看这一项OwningThread  ,它显示是当前拥有此锁的线程。用快捷键ALT+9打开线程与进程窗口,可以看到对应的线程ID。根据堆栈再根据你之前打印出来的线程ID,你就能定位到在哪一个线程发生了死锁 。

这里我们看到是1号线程在等2号线程占有的锁,而2号线程在等1号线程占有的锁,从而发生死锁。

以上的情况属于比较理想的情况,没有用到第三方的库或者开源库,实际使用中,情况则更复杂。我遇到就是三个线程在等一个线程的锁,而这个线程在等一个已经退出线程的锁,从而导致死锁。解决的办法还是显示出锁的信息,根据打出来的线程ID去定位。应该是退出线程非正常退出后,或者正常退出后没有RELEASE锁,从而导致死锁。


 

 




           

猜你喜欢

转载自blog.csdn.net/qq_44949789/article/details/89466675