調(diào)試場(chǎng)景
遠(yuǎn)程調(diào)試
使用WinDbg進(jìn)行遠(yuǎn)程調(diào)試是很容易的,而且有很多種可行的方法。在下文中,’調(diào)試服務(wù)器’指的是運(yùn)行在你所要調(diào)試的遠(yuǎn)程機(jī)器上的調(diào)試器。’調(diào)試客戶端’指的是控制當(dāng)前會(huì)話的調(diào)試器。
· 使用調(diào)試器:你需要CDB, NTSD或者WinDbg已經(jīng)安裝在遠(yuǎn)程機(jī)器上。WinDbg客戶端可以連接到CDB, NTSD或者WinDbg中的任何一個(gè)作為服務(wù)器,反之亦然。在客戶端和服務(wù)器直接可以選擇TCP或者命名管道作為通訊協(xié)議。
o 在服務(wù)器端的啟動(dòng)過程:
§ WinDbg –server npipe:pipe=pipename(注:可以允許多個(gè)客戶端連或
§ 從WinDbg內(nèi)部: .server npipe:pipe=pipename(注,連接單個(gè)客戶端)
你可以用多種協(xié)議開啟不同的服務(wù)會(huì)話。并且可用密碼來保護(hù)一個(gè)會(huì)話。
o 從客戶端連接:
§ WinDbg -remote npipe:server=Server, pipe=PipeName[,password=Password]
§ 從WinDbg內(nèi)部: File->Connect to Remote Session: for connection string, enter npipe:server=Server, pipe=PipeName [,password=Password]
· 使用Remote.exe: Remote.exe使用命名管道作為通訊的方式。如果你使用的是一個(gè)命令行接口的程序,比如KD,CDB或者NTSD。你可以使用remote.exe來遠(yuǎn)程調(diào)試。注意:使用@q(不是q)來退出客戶端,不用關(guān)掉服務(wù)端。
o 要啟動(dòng)一個(gè)服務(wù)端:
§ Remote.exe /s “cdp –p <pid>” test1
o 從客戶端連接:
§ Remote.exe /c <machinename> test1
上面的test1是我們所選擇的命名管道的名字。
服務(wù)端會(huì)顯示那個(gè)客戶端從那個(gè)服務(wù)器連接以及執(zhí)行過的命令。你可以使用‘qq’命令來退出服務(wù)端;或者使用File->Exit來退出客戶端。另外,如果要進(jìn)行遠(yuǎn)程調(diào)試,你必須屬于遠(yuǎn)程機(jī)器的”Debugger User”組并且服務(wù)器必須允許遠(yuǎn)程連接。
即時(shí)調(diào)試
在WinDbg的文檔的”Enabling Postmorten Debugging”部分對(duì)此有很詳細(xì)的討論。簡(jiǎn)而言之,你可以把WinDbg設(shè)置成默認(rèn)的即時(shí)調(diào)試器,命令就是:Windbg –I。這個(gè)命令實(shí)際上是把注冊(cè)表中 HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug的鍵值設(shè)置成WinDbg。如果要把WinDbg設(shè)置成為默認(rèn)的托管調(diào)試器,你需要顯示設(shè)置如下的注冊(cè)表鍵值:
- HKLM\Software\Microsoft\.NETFramework\DbgJITDebugLaunchSetting 設(shè)置成 2
- HKLM\Software\Microsoft\.NETFramework\DbgManagedDebugger 設(shè)置成Windbg.(注意其中的啟動(dòng)參數(shù)設(shè)置)
通過JIT的設(shè)置,當(dāng)一個(gè)應(yīng)用程序在不是調(diào)試的狀態(tài)下拋出了未處理的異常之時(shí),WinDbg就會(huì)被啟動(dòng)。
64位調(diào)試
所有這些調(diào)試器均支持在AMD64和IA64上的64位調(diào)試環(huán)境。
托管應(yīng)用程序的調(diào)試
WinDbg 6.3以后的版本支持在Widbey(VS2005和.net 2.0的內(nèi)部開發(fā)代號(hào)) .net CLR托管調(diào)試。在文檔中針對(duì)托管調(diào)試有很好的討論。需要注意的是,對(duì)于托管程序來說,沒有剛才所說的PDB(譯注:托管代碼實(shí)際上也是有PDB的,但是這個(gè)PDB實(shí)際上記錄了C#代碼和IL代碼的對(duì)應(yīng)關(guān)系以及相關(guān)的一些信息)的概念,因?yàn)樗械某绦蚨际蔷幾g成為ILASM。調(diào)試器通過CLR來查詢所需的附加信息。
有幾點(diǎn)需要注意:
你只能在托段函數(shù)的代碼被執(zhí)行過至少一次之后才能設(shè)置斷點(diǎn)。只有這樣它才能被編譯成匯編代碼。記住以下的幾點(diǎn):
o CLR有可能丟棄已經(jīng)編譯好的代碼,所以函數(shù)的入口地址有可能改變。
o 同樣的代碼有可能被多次編譯,如果多個(gè)應(yīng)用程序域沒有共享這段代碼的話。如果你設(shè)置了一個(gè)斷點(diǎn),它就會(huì)被設(shè)置在當(dāng)前線程(譯注:CLR的邏輯線程)所在的應(yīng)用程序域內(nèi)。
o 泛型的特殊實(shí)例可能導(dǎo)致同一個(gè)函數(shù)有不同的地址。.
o 當(dāng)跟蹤托管代碼的時(shí)候,你會(huì)需要穿越大段的CLR自己的代碼比如JIT編譯器的代碼,原因可能是你第一次進(jìn)入一個(gè)函數(shù),或者是你在托管和非托管代碼之間進(jìn)行切換。
調(diào)試Windows服務(wù)
使用WinDbg,你可以像調(diào)試其它應(yīng)用程序那樣調(diào)試Windows服務(wù)程序。即可以通過附加進(jìn)程的方法啟動(dòng)Windows服務(wù),也可以把WinDbg當(dāng)作一個(gè)即時(shí)調(diào)試器,并且在代碼中調(diào)用DbgBreakPoint
或者 DebugBreak
,或者在
x86
機(jī)器上加入一條
int 3
匯編指令。
調(diào)試異常
一個(gè)調(diào)試器會(huì)得到兩次的異常通知-第一次在應(yīng)用程序有機(jī)會(huì)處理異常之前(‘first chance exception’);如果應(yīng)用程序沒有處理這個(gè)異常,這時(shí)候調(diào)試器就會(huì)有機(jī)會(huì)來處理異常(‘second-chance exception’)。如果調(diào)試器沒有處理二次機(jī)會(huì)的異常,應(yīng)用程序就會(huì)退出。
.lastevent或者,!analyze –v命令會(huì)給你顯示異常的記錄以及異常拋出所在函數(shù)的堆棧跟蹤信息。
你也可以使用 .exr, .cxr以及 .ecxr命令來顯示異常和上下文記錄。同時(shí)需要注意的是,你也可以改變first-chance的處理選項(xiàng)。對(duì)應(yīng)的命令就是: sxe, sxd, sxn和sxi。
WinDbg的功能
調(diào)試器擴(kuò)展DLL
所謂的擴(kuò)展指的是一些DLL,你可以用在調(diào)試器內(nèi)調(diào)用并且執(zhí)行一些自定義的命令。這些DLL必須實(shí)現(xiàn)一些特定的函數(shù),并且要滿足一些需求,這樣才能被認(rèn)為是一個(gè)擴(kuò)展DLL。在下一篇文章內(nèi),我們將會(huì)了解到怎樣寫出一個(gè)擴(kuò)展DLL。所謂的bang(!)命令就是從你的擴(kuò)展DLL內(nèi)執(zhí)行的命令。注意這些DLL是被加載到調(diào)試器的進(jìn)程空間內(nèi)。
內(nèi)存轉(zhuǎn)儲(chǔ)文件
你可以使用轉(zhuǎn)儲(chǔ)功能來取得一個(gè)進(jìn)程的快照信息。一個(gè)mini-dump通常比較小,除非你使用了全內(nèi)存的minidump(.dump /mf)。通常能夠轉(zhuǎn)儲(chǔ)句柄信息也是很有用的,命令是 .dump/mfh。一個(gè)小型轉(zhuǎn)儲(chǔ)通常包含了所有的線程的堆棧以及一個(gè)已被加載的模塊的列表。一個(gè)全轉(zhuǎn)儲(chǔ)包含了更多的信息,比如進(jìn)程的堆。
崩潰轉(zhuǎn)儲(chǔ)分析
如果你的windows系統(tǒng)當(dāng)機(jī),那么它就會(huì)在一個(gè)文件中轉(zhuǎn)儲(chǔ)物理內(nèi)存中的數(shù)據(jù),以及所有的進(jìn)程信息??梢酝ㄟ^Control Panel ->System->Advanced->’Startup and Recovery’來配置。你也可以首先把WinDbg配置成為一個(gè)即時(shí)調(diào)試器,然后就可以取得任意一個(gè)非正常終止的進(jìn)程的轉(zhuǎn)儲(chǔ)(.dump)。注意,從轉(zhuǎn)儲(chǔ)文件中分析出代碼中的bug往往是一個(gè)復(fù)雜費(fèi)力的過程。
使用以下的步驟來分析一個(gè)轉(zhuǎn)儲(chǔ)文件:
1) 在WinDbg內(nèi),通過 File->’Open Crash Dump’, 指向轉(zhuǎn)儲(chǔ)文件。
2) WinDgb會(huì)給你顯示應(yīng)用程序崩潰之時(shí)所執(zhí)行的指令。
3) 正確設(shè)置你的符號(hào)文件目錄和源代碼目錄。如果你不能夠匹配正確的符號(hào)文件,想要弄清楚程序的邏輯是非常困難的。如果你能夠把符號(hào)文件匹配到正確版本的源代碼,這是就應(yīng)該很容易分析出Bug原因。注意,私有符號(hào)文件含有行號(hào)信息并且會(huì)盲目的顯示你源代碼中的對(duì)應(yīng)行而不進(jìn)行任何的檢查;如果你的源碼版本不對(duì),那么你就不能夠看到匹配匯編代碼的正確源碼。如果你僅僅有公有的符號(hào)文件,你會(huì)看到最后一個(gè)被調(diào)用的函數(shù)(棧上的)。
注意調(diào)試驅(qū)動(dòng)或者托管代碼是與此有很大不同的。參考《The Windows 2000 Device Driver Book》來獲得調(diào)試設(shè)備驅(qū)動(dòng)的技術(shù)。
WinDbg的常用設(shè)置
符號(hào)文件與文件夾
如果想更有效的調(diào)試,你需要符號(hào)文件。符號(hào)文件可以是老式的COFF格式或者就是PDB格式。PDB就是程序數(shù)據(jù)庫(kù)文件并且包含了公有符號(hào)。這些調(diào)試器內(nèi),你可以使用一系列的地址來讓調(diào)試器尋找已經(jīng)加載的二進(jìn)制文件的符號(hào)。
操作系統(tǒng)的符號(hào)文件一般存儲(chǔ)在%SYSTEMDIR%Symbols目錄。驅(qū)動(dòng)程序的符號(hào)文件(.DBG或.PDB)一般存儲(chǔ)在和驅(qū)動(dòng)文件(.sys 文件)相同的目錄下。私有符號(hào)文件包含的信息包括:函數(shù),局部以及全局變量,以及用來把匯編代碼和源代碼關(guān)聯(lián)起來的行號(hào)信息;對(duì)于客戶來說,符號(hào)文件一半是公有的-這些文件僅僅包括公有成員。
你可以通過File-Symbol File Path來設(shè)置符號(hào)文件目錄,或者使用 .sympath命令。如果想要添加到網(wǎng)絡(luò)上符號(hào)文件的引用,添加以下的內(nèi)容到你的 .sympath
SRV*downstream_store*http://msdl.microsoft.com/download/symbols
使用的命令就是:
.sympath+ SRV*c:\tmp*http://msdl.microsoft.com/download/symbols
C:\tmp就是download_store,所需要的符號(hào)文件會(huì)被下載存儲(chǔ)至此。注意這個(gè)符號(hào)服務(wù)器僅僅開放了公有的符號(hào)文件。
當(dāng)調(diào)試器把一個(gè)二進(jìn)制文件(DLL或exe)的時(shí)候,他會(huì)檢查比如文件名,時(shí)間戳以及校驗(yàn)值。如果你有符號(hào)信息,你就可以在調(diào)用棧上看到函數(shù)名和他們的參數(shù)。如果二進(jìn)制文件和PDB文件都來自于你自己的應(yīng)用程序,你就可以看到比如私有函數(shù),局部變量以及類型這類額外的信息。
源代碼路徑
你可以通過File->Source File Path來設(shè)置源碼路徑,或者使用.srcpath命令。如果你設(shè)置了代碼的路徑,當(dāng)你調(diào)試的時(shí)候,調(diào)試器會(huì)通過PDB文件的行號(hào)信息來顯示相匹配的源代碼。
斷點(diǎn),跟蹤
· 通過bp命令或者工具欄上的斷點(diǎn)圖片來設(shè)置軟斷點(diǎn)。
· 通過代碼比如DbgBreakPoint() 或者 KdBreakPoint()來設(shè)置硬斷點(diǎn)。
· 在擴(kuò)展DLL中使用跟蹤函數(shù)DbgPrint, KdPrint, OutputDebugString 來把輸出顯示在WinDbg的輸出窗口中。