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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,首先你需要打开 Windbg,并连接到你想要排查的系统。然后你可以使用 !locks 命令来查看系统中所有的锁,并用 !dlk 命令来检查锁的情况。 如果你想要更深入地排查死锁,你可以使用 !threads 命令来查看所有的线程,并使用 ~*k 命令来查看每个线程的栈。这样你就可以找到可能导致死锁的函数调用。 此外,你还可以使用 !analyze -v 命令来分析死锁的原因,它会对系统中的所有锁进行分析,并给出建议的解决方案。 最后,你可以使用 .dump /ma 命令来生成内存转储文件,然后使用 !analyze -v 命令来分析转储文件,这样你就可以在离线状态下排查死锁。 ### 回答2: Windbg是一款用于Windows调试的强大工具,可以用于排查死锁问题。下面将介绍如何使用Windbg来排查死锁问题。 首先,通过命令行方式打开Windbg,然后导入相关的符号文件。接下来,可以通过以下几个步骤来查找死锁: 1. 设置调试器为检测死锁模式。在命令行中输入"!locks"命令,这会显示当前系统上所有的锁资源情况。查看输出结果,可以找到被锁定的对象和相应的线程信息。 2. 定位死锁线程。使用"~*"命令列出所有的线程,然后使用"~#s"命令查看每个线程的堆栈信息。在堆栈中查找是否存在死锁线程的迹象,例如相互等待锁资源或无法进一步执行的情况。 3. 分析死锁原因。使用"!analyze"命令分析死锁信息,这将提供一些相关的调试信息,帮助我们找到问题的根源。通常,是由于资源的竞争导致死锁的发生。 4. 查看锁资源情况。通过"!handle"命令查看系统中所有的句柄信息,找到被死锁线程所持有的句柄。然后,可以使用"!object"命令查看句柄对应的对象信息,找到造成死锁的具体原因。 5. 解决死锁问题。根据分析结果,采取相应的解决措施来解决死锁问题。可能的解决办法包括:调整锁资源的使用顺序、增加锁资源的数量或使用更高级别的锁对象等。 通过以上步骤,我们可以利用Windbg定位和解决死锁问题。这个过程需要一定的调试经验和分析能力,但通过合理使用Windbg的调试命令,可以有效地排查死锁问题,提高系统的稳定性和性能。 ### 回答3: Windbg是一种强大的调试工具,可以用来分析和解决死锁问题。在排查死锁时,可以按照以下步骤进行操作: 1. 获取dump文件:首先,通过Windbg获取应用程序的dump文件,dump文件保存了应用程序在死锁发生时的内存和线程状态信息。 2. 加载dump文件:打开Windbg,选择“文件”菜单中的“打开转储文件”选项,然后选择要加载的dump文件。 3. 分析线程堆栈:使用Windbg命令"!analyze -v"分析线程堆栈。这个命令会提供线程堆栈的详细信息,包括当前线程和其它线程的调用栈。 4. 查找死锁引起的资源:使用Windbg的命令"!locks"来查找死锁引起的资源。这个命令会显示所有的锁、线程和资源之间的关系。通过查看这些信息,可以找到死锁的根本原因。 5. 跟踪线程执行路径:通过Windbg的命令"kb"或"~*"查看所有线程的执行路径,定位到正在等待某个资源的线程。 6. 分析死锁情况:根据线程堆栈和资源锁等信息,判断是什么原因导致了死锁的发生,比如是否存在互斥锁的竞争、资源的有界性等问题。 7. 修复死锁问题:根据分析结果,对死锁问题进行修复,可能需要修改代码逻辑、优化资源的使用方式、增加资源数量等手段来解决。 需要注意的是,在使用Windbg进行死锁排查时,需要对调试工具的使用有一定的经验和理解。同时,死锁问题的排查也可能是一项复杂的任务,需要仔细的分析和调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值