原文:http://blogs.msdn.com/b/kaevans/archive/2011/04/11/intro-to-windbg-for-net-developers.aspx
翻譯:cxfksword
當你的代碼發布為產品後,無論是在其他人的電腦中運行,還是部署到伺服器中,你通常不再能訪問到它的程式檔案,也不能觀察到代碼的當前運行情況和運行環境。當你的代碼在新的環境運行時,有很多因素會影響到程式的運行情況,如伺服器系統打了更新補丁,網路原則改變,防火牆規則限制,磁碟許可權配置等等。當代碼運行不正常時,你可能只能靠代碼中各處輸出的日誌來判斷運行情況。但只靠輸出日誌,你有時還是不能判斷故障出現的原因。
在不浪費客戶時間的同時做故障排除對你是個很大的挑戰,因為不會有客戶喜歡被一個搞技術的傢伙不斷問是點了那個按鈕操作了哪些步驟導致程式出錯的。boss也不會給你幾天甚至幾周的時間讓你慢慢排除故障,你必須現在就知道到底發生了什麼。
理想情況下,你應該能看到堆疊追蹤,能查看到當前的變數值,能調試代碼。事實證明你可以做到這點。。。而且不需要附加到客戶環境中!
下載WinDbg
下載Debugging Tools for Windows到你的本地開發機中,windbg是其中的一部分,如果你只需要windbg,在可以在“Common Utilities”中選擇“Debugging Tools for Windows“進入安裝。安裝程式會根據你電腦的cpu類型安裝對應的windbg,x86的電腦會安裝x86版本,x64的cpu電腦就會安裝x64版。如果你選擇的是“Redistributable
Packages“,就會為你下載全部三個版本(x86,x64,Itanium)。
WinDbg預設會安裝到c盤的“Program Files\Debugging Tools for Windows”檔案夾下,建議你把安裝目錄複寫到“d:\\debug”,這樣方便後面增加其他向外延展群組件。
安裝好後,目錄下的windbg.exe就是windbg程式
安裝PssCor2
下一步是載入Managed 程式碼擴充組件PssCor2。預設時,WinDbg只能用於調試Unmanaged 程式碼程式 ,而載入.net使用的SOS.dll向外延展群組件後,WinDbg就能調試託管程式了。WinDbg調試.net程式的另一個選擇是PssCor2,它是SOS.dll的超集,並提供了一些面對Managed 程式碼的額外功能,如查看託管線程,託管堆,CLR堆棧等等。
下載PssCor2並解壓到“d:\\debug”,以方便後面的調試使用。
設定符號檔案路徑(Symbol Path)
當你使用Visual Studio編譯器時,是否有留意到在bin/Debug檔案夾下會有.pdb尾碼的檔案?這些檔案包含有dll程式集的偵錯符號,pdb檔案並不包含有執行代碼,只是使調試工具能把代碼執行指令翻譯為正確的可識別字元。微軟提供了包含大量pdb檔案的公用伺服器,地址如下:
http://msdl.microsoft.com/download/symbols
在windbg中設定符號檔案路徑後,相關的pdb檔案會自動從伺服器下載下來並儲存到本地。你首先需要指定一個pdb檔案的儲存路徑,如“d:\\debug\symbols”。
開啟windbg程式,選擇“File->Symbol File Path…“,把下面的內容複寫進去儲存。
srv*d:\debug\symbols*http://msdl.microsoft.com/download/symbols
建立測試程式
我們先建立一個簡單的命令列程式用於測試:
using System;namespace Microsoft.PFE.Samples{ public class Program { static void Main() { Console.WriteLine("Enter a message:"); string input = Console.ReadLine(); Data d = new Data { ID = 5, Message = input, CurrentDateTime = System.DateTime.Now }; Console.WriteLine("You entered: " + d); } } public class Data { public int ID {get; set;} public string Message {get; set;} public DateTime CurrentDateTime {get; set;} public override string ToString() { Console.ReadLine(); return string.Format("ID:{0} {1} at {2}", ID, Message, CurrentDateTime.ToLongTimeString()); } }}
因為PssCor2隻能處理.Net 3.5以下的程式,所以在編譯前需要先把程式的環境改為.Net 3.5。假如是調試.NET 4.0的程式,可以下載PssCor4。編譯運行程式,輸入一個字串,看程式是否運行正常。
客戶抱怨說不知道為什麼程式需要按兩次enter鍵。程式並不按我們的預期工作,我們必須找到具體的原因。作為一個簡單的例子,我們能一眼看出代碼中的ToString()方法中多了一次ReadLine()導致的,但我們這次試下用windbg找出問題所在。
運行程式,輸入一個字串,按一次enter,當出現第二次輸入提示時,不要動!我們處於捕捉問題的關鍵點,我們需要做一個dump檔案。
建立dump檔案
在windows7和windows2008中,可以在工作管理員中直接建立dump檔案。只需開啟工作管理員,右鍵進程名並選擇“Create Dump File”。
dump檔案建立成功後,我們會看到提示:
dump檔案是當前進程的記憶體快照,dump檔案的大小會和進程使用的記憶體大小一樣,為了減少體積,你可以使用壓縮軟體進行壓縮。
還有另外的工具可以建立dump檔案,如Process Explorer from SysInternals,也只需要在任務管理中右鍵選擇“Full Dump”。
ADPlus和DebugDiag也可以建立dump檔案。ADPlus是windbg安裝目錄下的一個命令列程式,你可以用下面的命令建立一個dump檔案:
Adplus -quiet -hang -p 4332 -o d:\debug
4332是進程id,工作管理員預設是不顯示進程id的,要顯示出來,需要在windows工作管理員選擇“查看->選擇列”,勾選“PID(進程標識符)”。
開始使用WinDbg
現在我們有了程式dump檔案,開啟windbg程式,選擇菜單“File->Open Crash Dump”,並選擇剛建立的dump檔案,你會看到一些資訊:
Loading Dump File [D:\debug\program6.dmp]User Mini Dump File: Only registers, stack and portions of memory are availableSymbol search path is: srv*d:\debug\symbols*http://msdl.microsoft.com/download/symbolsExecutable search path is:Windows 7 Version 7600 MP (8 procs) Free x64Product: WinNt, suite: SingleUserTSMachine Name:Debug session time: Sun Feb 6 10:43:57.000 2011 (GMT-6)System Uptime: not availableProcess Uptime: 0 days 1:05:48.000.........................ntdll!NtRequestWaitReplyPort+0xa:00000000`76d2ff7a c3 ret
在上面的文字中,你可以看到dump檔案的路徑,符號檔案的尋找路徑等資訊。而程式最下方有個輸入框,你可以在上面輸入命令。
顯示模組
讓我們試下顯示程式已載入了哪些模組。在視窗最下方的輸入框中,輸入“lm”命令。
0:000> lmstart end module name00000000`00120000 00000000`00128000 program (deferred)00000000`742b0000 00000000`74379000 msvcr80 (deferred)00000000`76ac0000 00000000`76bba000 user32 (deferred)00000000`76bc0000 00000000`76cdf000 kernel32 (pdb symbols) d:\debug\symbols\kernel32.pdb\D5E268B5DD1048A1BFB011C744DD3DFA2\kernel32.pdb00000000`76ce0000 00000000`76e8b000 ntdll (pdb symbols) d:\debug\symbols\ntdll.pdb\0F7FCF88442F4B0E9FB51DC4A754D9DE2\ntdll.pdb000007fe`f3fb0000 000007fe`f4134000 mscorjit (deferred)000007fe`f5030000 000007fe`f5f0b000 mscorlib_ni (deferred)000007fe`f7650000 000007fe`f7ffe000 mscorwks (deferred)000007fe`f8010000 000007fe`f80a0000 mscoreei (deferred)000007fe`f80a0000 000007fe`f810f000 mscoree (deferred)000007fe`fcb70000 000007fe`fcb7f000 CRYPTBASE (deferred)000007fe`fcc40000 000007fe`fcc4f000 profapi (deferred)000007fe`fcf20000 000007fe`fcf8b000 KERNELBASE (deferred)000007fe`fd0e0000 000007fe`fd2e2000 ole32 (deferred)000007fe`fd4d0000 000007fe`fd59a000 usp10 (deferred)000007fe`fd6f0000 000007fe`fe476000 shell32 (deferred)000007fe`fe480000 000007fe`fe4ae000 imm32 (deferred)000007fe`fe840000 000007fe`fe84e000 lpk (deferred)000007fe`fe9d0000 000007fe`feaab000 advapi32 (deferred)000007fe`feb50000 000007fe`fec7e000 rpcrt4 (deferred)000007fe`fec80000 000007fe`fecf1000 shlwapi (deferred)000007fe`fed00000 000007fe`fed67000 gdi32 (deferred)000007fe`fee10000 000007fe`fef19000 msctf (deferred)000007fe`fef20000 000007fe`fefbf000 msvcrt (deferred)000007fe`fefd0000 000007fe`fefef000 sechost (deferred)
在上面的模組列表中,你需要關注的是mscorwks是否存在,PssCor2隻能用於.NET 3.5的程式,假如是.NET 4.0程式,就看不到mscorwks模組。
對於SharePoint開發人員,假如你正在偵錯工具特性如 receivers 和 event 處理為什麼不觸發,lm將是很好用的命令。通過上面的列表,你能知道有哪些模組沒載入進來,可能是配置不正確導致的,這樣能大大地減少你尋找問題的範圍。對於ASP.NET開發人員,會有助於尋找HttpModule不觸發的原因,可能是web.config配置不正確。
載入PssCor2
要把PssCor2向外延展群組件載入入windbg,需使用下面的命令:
.load d:\debug\psscor2\amd64\psscor2.dll
我的電腦是64位的,所以我載入了AMD64 版本的PssCor2.dll。載入的PssCor2版本必須和dump檔案進程所在伺服器的架構相一致,假如你是調試x86程式的dump檔案,你就必須載入x86版本的PssCor2.dll。
輸入下面的命令,以確認PssCor2是否載入成功:
!help
正確會輸入下面的內容:
0:000> .load d:\debug\psscor2\amd64\psscor2.dll0:000> !help-------------------------------------------------------------------------------PSSCOR is a debugger extension DLL designed to aid in the debugging of managedprograms. Functions are listed by category, then roughly in order ofimportance. Shortcut names for popular functions are listed in parenthesis.Type "!help " for detailed info on that function. Object Inspection Examining code and stacks----------------------------- -----------------------------DumpObj (do) ThreadsDumpArray (da) CLRStackDumpStackObjects (dso) IP2MDDumpAllExceptions (dae) BPMDDumpHeap UDumpVC DumpStackGCRoot EEStackObjSize GCInfoFinalizeQueue EHInfoPrintException (pe) COMStateTraverseHeapDumpField (df)DumpDynamicAssemblies (dda)GCRefDumpColumnNames (dcn)DumpRequestQueuesDumpUMServiceExamining CLR data structures Diagnostic Utilities----------------------------- -----------------------------DumpDomain VerifyHeapEEHeap DumpLogName2EE FindAppDomainSyncBlk SaveModuleDumpThreadConfig (dtc) SaveAllModules (sam)DumpMT GCHandlesDumpClass GCHandleLeaksDumpMD VMMapToken2EE VMStatEEVersion ProcInfoDumpModule StopOnException (soe)ThreadPool MinidumpModeDumpHttpRuntime FindDebugTrueDumpIL FindDebugModulesPrintDateTime AnalysisDumpDataTables CLRUsageDumpAssembly CheckCurrentException (cce)RCWCleanupList CurrentExceptionName (cen)PrintIPAddress VerifyObjDumpHttpContext HeapStatASPXPages GCWhereDumpASPNETCache (dac) ListNearObj (lno)DumpSigDumpMethodSig OtherDumpRuntimeTypes -----------------------------ConvertVTDateToDate (cvtdd) FAQConvertTicksToDate (ctd)DumpRequestTableDumpHistoryTableDumpBucketsGetWorkItemsDumpXmlDocument (dxd)DumpCollection (dc)Examining the GC history-----------------------------HistInitHistStatsHistRootHistObjHistObjFindHistClear
mscordacwks.dll
我喜歡在伺服器中建立程式的dump檔案,然後把dump檔案轉移到自己的windows7開發機上進行調試。假如伺服器是Windows Server 2008 R2的作業系統,當我在本地開發機使用psscor2時,很容易遇到下面的錯誤:
CLRDLL: CLR DLL load disabledFailed to load data access DLL, 0x80004005Verify that 1) you have a recent build of the debugger (6.2.14 or newer) 2) the file mscordacwks.dll that matches your version of mscorwks.dll is in the version directory 3) or, if you are debugging a dump file, verify that the file mscordacwks___.dll is on your symbol path. 4) you are debugging on the same architecture as the dump file. For example, an IA64 dump file must be debugged on an IA64 machine.You can also run the debugger command .cordll to control the debugger'sload of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.If that succeeds, the PSSCOR command should work on retry.If you are debugging a minidump, you need to make sure that your executablepath is pointing to mscorwks.dll as well.
通過bing搜尋發現一篇相關的部落格文章how to work around the mscordacwks issue,文章指出需要把伺服器的mscordacwks檔案拷貝到windbg程式目錄下。mscordacwks在我的Windows
Server 2008 R2伺服器上的版本是4952,所以我把伺服器上的mscordacwks拷貝到windbg目錄,並重新命名為“mscordacwks_AMD64_AMD64_2.0.50727.4952.dll“。mscordacwks在伺服器的路徑是“”C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscordacwks.dll”,假如你不知道正確的重新命名規則,你可以輸入下面的命令,輸出會提示需要載入的mscordacwks命名。
0:000> .cordll -ve -u -lCLR DLL status: No load attempts0:000> !threadsCLRDLL: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscordacwks.dll:2.0.50727.3053 f:0doesn't match desired version 2.0.50727.3625 f:0CLRDLL: Unable to find mscordacwks_AMD64_AMD64_2.0.50727.4952.dll by mscorwks searchCLRDLL: Unable to find 'mscordacwks_AMD64_AMD64_2.0.50727.4952.dll' on the pathCLRDLL: Unable to get version info for 'd:\debug\symbols\mscorwks.dll\4E154C985a9000\mscordacwks_AMD64_AMD64_2.0.50727.4952.dll', Win32 error 0n87CLRDLL: ERROR: Unable to load DLL mscordacwks_AMD64_AMD64_2.0.50727.4952.dll, Win32 error 0n87Failed to load data access DLL, 0x80004005Verify that 1) you have a recent build of the debugger (6.2.14 or newer) 2) the file mscordacwks.dll that matches your version of mscorwks.dll is in the version directory 3) or, if you are debugging a dump file, verify that the file mscordacwks___.dll is on your symbol path. 4) you are debugging on the same architecture as the dump file. For example, an IA64 dump file must be debugged on an IA64 machine.You can also run the debugger command .cordll to control the debugger'sload of mscordacwks.dll. .cordll -ve -u -l will do a verbose reload.If that succeeds, the PSSCOR command should work on retry.If you are debugging a minidump, you need to make sure that your executablepath is pointing to mscorwks.dll as well.
重新命名後,再次輸入上面的命令,會顯示載入成功提示。
0:000> .cordll -ve -u -lCLR DLL status: Loaded DLL mscordacwks_AMD64_AMD64_2.0.50727.4952.dl
檢查CRL堆棧
要查看CLR堆棧內容,輸入下面命令:
!clrstack
輸出如下:
0:000> !clrstackOS Thread Id: 0xa48 (0)*** WARNING: Unable to verify checksum for mscorlib.ni.dllChild-SP RetAddr Call Site000000000012e910 000007fef5a910e9 DomainNeutralILStubClass.IL_STUB(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)000000000012ea30 000007fef5a91202 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Int32, Int32 ByRef)000000000012ea90 000007fef538065a System.IO.__ConsoleStream.Read(Byte[], Int32, Int32)000000000012eaf0 000007fef53a28ca System.IO.StreamReader.ReadBuffer()000000000012eb40 000007fef5a9435f System.IO.StreamReader.ReadLine()000000000012eb90 000007ff0017015b System.IO.TextReader+SyncTextReader.ReadLine()000000000012ebf0 000007fef791d502 Program.Main()
真棒!你現在可以看到堆棧的調用情況了。我們可以立即看到程式進入了Program.Main函數,調用Console.ReadLine並等待使用者的輸入。
結論
本篇文章只是簡單介紹windbg的使用,假如你想全域的瞭解windbg,並如何使用windbg做故障排除,可以看下Tess Ferrandez的視頻教程“Debugging .NET Applications with WinDbg“。
擴充閱讀:
Process Explorer from SysInternals
PssCor2 Debugging Extension
Download the Debugging Tools for Windows
SOS.dll (SOS Debugging Extension)
“Failed
to load data access DLL, 0×80004005” – OR – What is mscordacwks.dll?
DebugDiag
Getting started with windbg – part I
Getting started with windbg – part II