windbg定位线程死锁和句柄泄露

网上搜了两篇文章,一篇介绍windbg定位线程死锁,一篇介绍windbg定位句柄泄露,为了以后更好的学习,记录了下来。

一,线程死锁

#include <windows.h >

CRITICAL_SECTION cs1;
CRITICAL_SECTION cs2;

DWORD __stdcall thread1(LPVOID lp)
{
	EnterCriticalSection(&cs1);
	Sleep(10);
	EnterCriticalSection(&cs2);

	return 0;
}

DWORD  __stdcall thread2(LPVOID lp)
{
	EnterCriticalSection(&cs2);
	Sleep(10);
	EnterCriticalSection(&cs1);

	return 0;
}

int main()
{
	InitializeCriticalSection(&cs1);
	InitializeCriticalSection(&cs2);

	CreateThread(NULL, 0, thread1, 0, 0, NULL);
	CreateThread(NULL, 0, thread2, 0, 0, NULL);

	system("pause");
	return 0;

}

先用~*kb查看下所有的线程堆栈:

0:003> ~*kb   0  Id: 1a98.24c Suspend: 1 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr  Args to Child              
0012fddc 7c92df5a 7c8025db 00000044 00000000 ntdll!KiFastSystemCallRet
0012fde0 7c8025db 00000044 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0012fe44 7c802542 00000044 ffffffff 00000000 kernel32!WaitForSingleObjectEx+0xa8
0012fe58 7854bd40 00000044 ffffffff 00000000 kernel32!WaitForSingleObject+0x12
0012fedc 7854c702 00000000 00392b98 00392de0 MSVCR90!_dospawn+0x1d1 [f:\dd\vctools\crt_bld\self_x86\crt\src\dospawn.c @ 215]
0012ff00 7854c84b 00000000 00392b98 0012ff5c MSVCR90!comexecmd+0x60 [f:\dd\vctools\crt_bld\self_x86\crt\src\spawnve.c @ 137]
0012ff38 7854cc71 00000000 00392b98 0012ff5c MSVCR90!_spawnve+0x12a [f:\dd\vctools\crt_bld\self_x86\crt\src\spawnve.c @ 273]
0012ff70 004010a8 004020f4 00000001 00401218 MSVCR90!system+0x8e [f:\dd\vctools\crt_bld\self_x86\crt\src\system.c @ 87]
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffc0 7c817077 00300031 0032002d 7ffdc000 test2+0x10a8
0012fff0 00000000 00401360 00000000 78746341 kernel32!BaseProcessStart+0x23   1  Id: 1a98.1588 Suspend: 1 Teb: 7ffde000 Unfrozen
ChildEBP RetAddr  Args to Child              
0050ff14 7c92df5a 7c939b23 0000002c 00000000 ntdll!KiFastSystemCallRet
0050ff18 7c939b23 0000002c 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0050ffa0 7c921046 00403370 0040101d 00403370 ntdll!RtlpWaitForCriticalSection+0x132
0050ffa8 0040101d 00403370 000203a8 7c80b729 ntdll!RtlEnterCriticalSection+0x46
WARNING: Stack unwind information not available. Following frames may be wrong.
0050ffec 00000000 00401000 00000000 00000000 test2+0x101d   2  Id: 1a98.185c Suspend: 1 Teb: 7ffdd000 Unfrozen
ChildEBP RetAddr  Args to Child              
0060ff14 7c92df5a 7c939b23 00000034 00000000 ntdll!KiFastSystemCallRet
0060ff18 7c939b23 00000034 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0060ffa0 7c921046 00403388 0040104d 00403388 ntdll!RtlpWaitForCriticalSection+0x132
0060ffa8 0040104d 00403388 000203a8 7c80b729 ntdll!RtlEnterCriticalSection+0x46
WARNING: Stack unwind information not available. Following frames may be wrong.
0060ffec 00000000 00401030 00000000 00000000 test2+0x104d#  3  Id: 1a98.159c Suspend: 1 Teb: 7ffdb000 Unfrozen
ChildEBP RetAddr  Args to Child              
003dffc8 7c972119 00000005 00000004 00000001 ntdll!DbgBreakPoint
003dfff4 00000000 00000000 00000000 00000000 ntdll!DbgUiRemoteBreakin+0x2d


我们注意到1号线程的线程堆栈是从ntdll!RtlEnterCriticalSection中开始的,线程在此开始等待(造成死锁),其前面地址:0060ffa8 0040104d 00403388 000203a8 7c80b729中第三个地址00403388为其锁的地址。

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

!*kb:查看*线程的堆栈情况。

~*s:转换至*线程。

0:003> ~1kb
ChildEBP RetAddr  Args to Child              
0050ff14 7c92df5a 7c939b23 0000002c 00000000 ntdll!KiFastSystemCallRet
0050ff18 7c939b23 0000002c 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0050ffa0 7c921046 00403370 0040101d 00403370 ntdll!RtlpWaitForCriticalSection+0x132
0050ffa8 0040101d 00403370 000203a8 7c80b729 ntdll!RtlEnterCriticalSection+0x46
WARNING: Stack unwind information not available. Following frames may be wrong.
0050ffec 00000000 00401000 00000000 00000000 test2+0x101d
0:003> !cs 00403370 
-----------------------------------------
Critical section   = 0x00403370 (test2+0x3370)
DebugInfo          = 0x7c99e9e0
LOCKED
LockCount          = 0x1
OwningThread       = 0x0000185c
RecursionCount     = 0x1
LockSemaphore      = 0x2C
SpinCount          = 0x00000000

其中,LockCount:锁的个数;   OwningThread:00403370(锁地址)的拥有线程的地址;

通过Process and Thread可以查看到0x0000185c属于2号线程,这意思就是1号线程等待的临界区拥有者是2号线程,那么同样我们对2号线程进行分析:

0:003> ~2kb
ChildEBP RetAddr  Args to Child              
0060ff14 7c92df5a 7c939b23 00000034 00000000 ntdll!KiFastSystemCallRet
0060ff18 7c939b23 00000034 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
0060ffa0 7c921046 00403388 0040104d 00403388 ntdll!RtlpWaitForCriticalSection+0x132
0060ffa8 0040104d 00403388 000203a8 7c80b729 ntdll!RtlEnterCriticalSection+0x46
WARNING: Stack unwind information not available. Following frames may be wrong.
0060ffec 00000000 00401030 00000000 00000000 test2+0x104d
0:003> !cs 00403388 
-----------------------------------------
Critical section   = 0x00403388 (test2+0x3388)
DebugInfo          = 0x7c99e9c0
LOCKED
LockCount          = 0x1
OwningThread       = 0x00001588
RecursionCount     = 0x1
LockSemaphore      = 0x34
SpinCount          = 0x00000000
0:003> ~~[0x00001588]
   1  Id: 1a98.1588 Suspend: 1 Teb: 7ffde000 Unfrozen
      Start: test2+0x1000 (00401000) 
      Priority: 0  Priority class: 32  Affinity: f

通过Process and Thread可以查看到0x00001588属于1号线程,这意思就是2号线程等待的临界区拥有者是1号线程。

具体参考博客:http://blog.csdn.net/hgy413/article/details/7572097

 

二:句柄泄露
1,首先先上代码实例:

#include "stdafx.h"
#include <windows.h>
void fun1(void);
void fun2(void);
void fun3(void);
void fun4(void);
int main(int argc, char* argv[])
{
      while(1)
      {
            fun1();
            fun2();
            Sleep(100);
      }
      return 0;
}
void fun1(void)
{
      fun3();
}
void fun2(void)
{
      fun4();
}
void fun3(void)
{
      HANDLE hEvent;
      hEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
      CloseHandle(hEvent);
}
void fun4(void)
{
      HANDLE hEvent2;
      hEvent2 = CreateEvent(NULL,TRUE,TRUE,NULL);//这里只打开但是没关闭句柄
}

2、windbg调试

1)找到windbgs安装目录下的gflags.exe工具,该工具可用来打开windows自带的一些调试选项,具体gflags.exe的详细使用可以查看windbg帮助

这里我们设置勾上application verifiwer,该工具主要可用来对程序做一些稳定性的检测,本次调试主要用于保存栈的相关信息。同时设置stack backtrace即栈的大小为10.

2)运行windbg,打开第一步编译的程序,并使其跑起来;此时你查看任务管理器中的句柄信息,会发行相应进程句柄一直在增加。

3)windbg用ctrl+break命令中断进程运行,用!htrace -enable命令开启句柄检测;htrace提供了进行句柄相关检测的命令,可查看windbg帮助。

同时用g命令让程序运行。

4)再次中断进程,使用!htrace -snapshot命令,获得此时进程句柄的镜像。并再次让程序运行。

5)第三次中断进程运行,我们再使用!htrace -diff命令获得当前句柄状态与第4步 snapshot镜像句柄的差异;

我们可以发现:新增很多打开的句柄,平常情况下这些打开的句柄有可能不是泄露,需要具体分析,但是本次示例程序太简单,所以刚好所有打开的句柄都属于泄露的。

6)我们使用lsa 传递指定位置对应的代码,lsa  handlew2!fun4+0x0000002e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值