windbg的使用四(Windbg檢查死鎖 )


Windbg定位死鎖

  (2012-11-09 15:28:55)
標簽: 

雜談

分類: 測試

【轉】原文鏈接 http://blog.csdn.net/hgy413/article/details/7572097#comments

先上個代碼,自己隨手寫的:

  1. #include    
  2.   
  3. CRITICAL_SECTION cs1;  
  4. CRITICAL_SECTION cs2;  
  5.   
  6. DWORD __stdcall thread1(LPVOID lp)  
  7. {  
  8.     EnterCriticalSection(&cs1);  
  9.     Sleep(10);  
  10.     EnterCriticalSection(&cs2);  
  11.   
  12.     return 0;  
  13. }  
  14.   
  15. DWORD  __stdcall thread2(LPVOID lp)  
  16. {  
  17.     EnterCriticalSection(&cs2);  
  18.     Sleep(10);  
  19.     EnterCriticalSection(&cs1);  
  20.   
  21.     return 0;  
  22. }  
  23.   
  24. int main()  
  25. {  
  26.     InitializeCriticalSection(&cs1);  
  27.     InitializeCriticalSection(&cs2);  
  28.   
  29.     CreateThread(NULL, 0, thread1, 0, 0, NULL);  
  30.     CreateThread(NULL, 0, thread2, 0, 0, NULL);  
  31.   
  32.     system("pause");  
  33.     return 0;  
  34.   
  35. }  


運行,生成release版本,去掉pdb,運行,程序停住了,windbg加載到進程,

先用~*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. 我們注意到1號線程的線程堆棧是從ntdll!RtlEnterCriticalSection中開始的,那么ntdll!RtlEnterCriticalSection又是什么函數的入口呢,首先猜到的是EnterCriticalSection,這個函數是kernel32.dll中的,為了驗證猜測,我們用dump查看到kernel32.dll的導出函數:  

果然如此,

1. !cs

!cs 擴展顯示一個或多個臨界區(critical section)或者整個臨界區樹

前面說的ntdll!RtlEnterCriticalSection的第一個參數是臨界區的地址,事實上用uf反匯編它,可以看到是ret 4,說明就只有一個參數

那么,

!cs Address 指定要顯示的臨界區地址。如果省略該參數,調試器顯示當前進程中所有臨界區。
  1. 0:003> ~1kb  
  2. ChildEBP RetAddr  Args to Child                
  3. 0050ff14 7c92df5a 7c939b23 0000002c 00000000 ntdll!KiFastSystemCallRet  
  4. 0050ff18 7c939b23 0000002c 00000000 00000000 ntdll!NtWaitForSingleObject+0xc  
  5. 0050ffa0 7c921046 00403370 0040101d 00403370 ntdll!RtlpWaitForCriticalSection+0x132  
  6. 0050ffa8 0040101d 00403370 000203a8 7c80b729 ntdll!RtlEnterCriticalSection+0x46  
  7. WARNING: Stack unwind information not available. Following frames may be wrong.  
  8. 0050ffec 00000000 00401000 00000000 00000000 test2+0x101d  
  9. 0:003> !cs 00403370   
  10. -----------------------------------------  
  11. Critical section   = 0x00403370 (test2+0x3370)  
  12. DebugInfo          = 0x7c99e9e0  
  13. LOCKED  
  14. LockCount          = 0x1  
  15. OwningThread       = 0x0000185c  
  16. RecursionCount     = 0x1  
  17. LockSemaphore      = 0x2C  
  18. SpinCount          = 0x00000000  
  1.    
  1. 這里LockCount為1意思為除了一個線程擁有它外,另外還有一個線程在等待它,它是由EnterCriticalSection增加,LeaveCriticalSection來減小的,比如我再加一點代碼:  
  1. DWORD  __stdcall thread3(LPVOID lp)  
  2. {  
  3.     EnterCriticalSection(&cs2);  
  4.     Sleep(10);  
  5.     EnterCriticalSection(&cs1);  
  6.   
  7.     return 0;  
  8. }  
  9.   
  10. int main()  
  11. {  
  12.     InitializeCriticalSection(&cs1);  
  13.     InitializeCriticalSection(&cs2);  
  14.   
  15.     CreateThread(NULL, 0, thread1, 0, 0, NULL);  
  16.     CreateThread(NULL, 0, thread2, 0, 0, NULL);  
  17.     CreateThread(NULL, 0, thread3, 0, 0, NULL);  
  18.   
  19.     system("pause");  
  20.     return 0;  
  21.   
  22. }  

這時運行windbg:

  1. 0:004> ~1kb  
  2. ChildEBP RetAddr  Args to Child                
  3. 0051fe48 7c92df5a 7c939b23 00000034 00000000 ntdll!KiFastSystemCallRet  
  4. 0051fe4c 7c939b23 00000034 00000000 00000000 ntdll!NtWaitForSingleObject+0xc  
  5. 0051fed4 7c921046 00417140 00411420 00417140 ntdll!RtlpWaitForCriticalSection+0x132  
  6. *** WARNING: Unable to verify checksum for D:\Project1\test2\Debug\test2.exe  
  7. 0051fedc 00411420 00417140 00000000 00000000 ntdll!RtlEnterCriticalSection+0x46  
  8. 0051ffb4 7c80b729 00000000 00000000 00000000 test2!thread1+0x50 [d:\project1\test2\test2\test2.cpp @ 10]  
  9. 0051ffec 00000000 00411122 00000000 00000000 kernel32!BaseThreadStart+0x37  
  10. 0:004> !cs 00417140  
  11. -----------------------------------------  
  12. Critical section   = 0x00417140 (test2!cs2+0x0)  
  13. DebugInfo          = 0x7c99ea00  
  14. LOCKED  
  15. LockCount          = 0x2  
  16. OwningThread       = 0x00001f60  
  17. RecursionCount     = 0x1  
  18. LockSemaphore      = 0x34  
  19. SpinCount          = 0x00000000  

可以發現LockCount變成了2,如果臨界區是有信號的,則顯示NOT LOCKED(值為-1)

OwningThread表示擁有這個臨界區的線程ID,RecursionCount表示擁有線程調了幾次EnterCriticalSection,這其實也影響到了LockCount,如果擁有線程多調用一次EnterCriticalSection,那么 LockCount也會相應加1,因為LockCount標識了任意線程調用EnterCriticalSection請求這個互斥量的次數減1,(所以0-1=-1為NOT LOCKED)當然,前面如果調用了LeaveCriticalSection,那么 LockCount也會相應減1

我們繼續看原有的程序:

~~[TID]線程 ID 為 TID 的線程。(中括號是必需的,而且在第二個~和左括號間不能有空格)

  1. 0:003> ~~[0x0000185c]  
  2.    2  Id: 1a98.185c Suspend: 1 Teb: 7ffdd000 Unfrozen  
  3.       Start: test2+0x1030 (00401030)   
  4.       Priority: 0  Priority class: 32  Affinity: f  

 這意思就是1號線程等待的臨界區擁有者是2號線程,那么同樣我們對2號線程進行分析:

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

原來2號線程等待的臨界區擁有者是1號線程,所以經典的死鎖現象出現了!!!!!!!!!!!!!!!!!!!!!!!!

 

下面繼續介紹下!cs的擴展:

!cs -s 如果可能的話,顯示每個臨界區的初始堆棧回溯。
!cs -l 僅顯示鎖定的臨界區。
  1. 0:003> !cs -l   
  2. -----------------------------------------  
  3. DebugInfo          = 0x7c99e9c0  
  4. Critical section   = 0x00403388 (test2+0x3388)  
  5. LOCKED  
  6. LockCount          = 0x1  
  7. OwningThread       = 0x00001588  
  8. RecursionCount     = 0x1  
  9. LockSemaphore      = 0x34  
  10. SpinCount          = 0x00000000  
  11. -----------------------------------------  
  12. DebugInfo          = 0x7c99e9e0  
  13. Critical section   = 0x00403370 (test2+0x3370)  
  14. LOCKED  
  15. LockCount          = 0x1  
  16. OwningThread       = 0x0000185c  
  17. RecursionCount     = 0x1  
  18. LockSemaphore      = 0x2C  
  19. SpinCount          = 0x00000000  


!cs starAddress EndAddress指定要搜索臨界區的地址范圍

  1. 0:003> !cs 0x00400000 0x00500000  
  2. -----------------------------------------  
  3. DebugInfo          = 0x7c99e9c0  
  4. Critical section   = 0x00403388 (test2+0x3388)  
  5. LOCKED  
  6. LockCount          = 0x1  
  7. OwningThread       = 0x00001588  
  8. RecursionCount     = 0x1  
  9. LockSemaphore      = 0x34  
  10. SpinCount          = 0x00000000  
  11. -----------------------------------------  
  12. DebugInfo          = 0x7c99e9e0  
  13. Critical section   = 0x00403370 (test2+0x3370)  
  14. LOCKED  
  15. LockCount          = 0x1  
  16. OwningThread       = 0x0000185c  
  17. RecursionCount     = 0x1  
  18. LockSemaphore      = 0x2C  
  19. SpinCount          = 0x00000000  
!cs -o 對所有顯示出來的已鎖定的臨界區,顯示所有者的堆棧。
!cs -?顯示該命令的幫助文本。
  1. 0:003> !cs -?  
  2. !cs [-s] [-l] [-o]                   - dump all the active critical sections in the current process.  
  3. !cs [-s] [-o] address                - dump critical section at this address.  
  4. !cs [-s] [-l] [-o] address1 address2 - dump all the active critical sections in this range.  
  5. !cs [-s] [-o] -d address             - dump critical section corresponding to DebugInfo at this address.  
  6.   
  7. "-s" will dump the critical section initialization stack trace if it is available.  
  8.   
  9. "-l" will dump only the locked critical sections.  
  10.   
  11. "-o" will dump the critical section owner's stack.  

注意!

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



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