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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: