你可以從微軟網(wǎng)站上下載到的調(diào)試器:
· KD-內(nèi)核調(diào)試器。你可以用它來(lái)調(diào)試藍(lán)屏一類(lèi)的系統(tǒng)問(wèn)題。如果是開(kāi)發(fā)設(shè)備驅(qū)動(dòng)程序是少不了它的。
· CDB-命令行調(diào)試器。這是一個(gè)命令行程序
· NTSD-NT調(diào)試器。這是一個(gè)用戶(hù)模式調(diào)試器,可以用來(lái)調(diào)試用戶(hù)模式應(yīng)用程序。它實(shí)際上是一個(gè)CDB的windows UI增強(qiáng)。
· WinDbg-用一個(gè)漂亮的UI包裝了KD和NTSD。WinDbg即可以調(diào)試內(nèi)核模式,也可以調(diào)試用戶(hù)模式程序。
· VS, VS.net-使用同KD和NTSD相同的調(diào)試引擎,并且相比于同樣用于調(diào)試目的的WinDbg,提供了功能更豐富的界面。
WinDbg實(shí)際上包裝了NTSD和KD并且提供了一個(gè)更好用的用戶(hù)界面。它也提供了命令行開(kāi)關(guān),比如最小化啟動(dòng)(-m),附加到一PID指定的進(jìn)程(-p)以及自動(dòng)打開(kāi)崩潰文件(-z)。它支持三種類(lèi)型的命令。
· Regular commands(比如: k) 用來(lái)調(diào)試進(jìn)程
· Dot commands(比如:.sympath)用來(lái)控制調(diào)試器
· Extension commands(比如: !handle)-這些命令屬于可以用來(lái)添加到WinDbg的自定義命令;它們用擴(kuò)展DLL的輸出函數(shù)來(lái)實(shí)現(xiàn)。
PDB文件
PDB文件, 是鏈接器生成程序數(shù)據(jù)庫(kù)文件(Program database files)。私有的PDB文件包括私有以及公有符號(hào),源代碼行號(hào),類(lèi)型,局部以及全局變量。公有的PDB文件不包含類(lèi)型,局部變量以及源代碼行號(hào)信息。
配置WinDbg
運(yùn)行WinDbg->菜單->File->Symbol File Path->按照下面的方法設(shè)置_NT_SYMBOL_PATH變量:
在彈出的框中輸入“C:\MyCodesSymbols; SRV*C:\MyLocalSymbols*http://msdl.microsoft.com/download/symbols”(按照這樣設(shè)置,WinDbg將先從本地文件夾C:\MyCodesSymbols中查找Symbol,如果找不到,則自動(dòng)從MS的Symbol Server上下載Symbols)。另一種做法是從這個(gè)Symbol下載地址中http://www.microsoft.com/whdc/devtools/debugging/symbolpkg.mspx,下載相應(yīng)操作系統(tǒng)所需要的完整的Symbol安裝包,并進(jìn)行安裝,例如我將其安裝在D:\WINDOWS\Symbols,在該框中輸入“D:\WINDOWS\Symbols”。(這里要注意下載的Symbols的版本一定要正確,在我的Win2003+Sp1上,我曾經(jīng)以為安裝Win2003+Sp2的Symbols可能會(huì)牛×點(diǎn),但結(jié)果證明我錯(cuò)了,用WinDbg打開(kāi)可執(zhí)行文件時(shí),提示“PDB symbol for mscorwks.dll not loaded;Defaulted to export symbols for ntdll.dll”的錯(cuò)誤,我有重新裝上Win2003+Sp1的Symbols, 現(xiàn)在一切運(yùn)行正常^_^)
set _NT_SYMBOL_PATH=srv*C:\MySymbols*http://msdl.microsoft.com/download/symbols
WINDBG命令
調(diào)試前的必備工作
在開(kāi)始調(diào)試前首先要做的工作是設(shè)置好符號(hào)(Symbols)路徑。沒(méi)有符號(hào),你看到的調(diào)用堆棧基本上毫無(wú)意義。Microsoft的操作系統(tǒng)符號(hào)文件(PDB)是對(duì)外公開(kāi)的。另外請(qǐng)注意在編譯你自己的程序選擇生成PDB文件的選項(xiàng)。如果設(shè)置好符號(hào)路徑后,調(diào)用堆棧看起來(lái)還是不對(duì)。可以使用lm, !sym noisy, !reload 等命令來(lái)驗(yàn)證符號(hào)路徑是否正確。
Windbg也支持源碼級(jí)的調(diào)試。在開(kāi)始源碼調(diào)試前,你需要用.srcpath設(shè)置源代碼路徑。如果你是在生成所執(zhí)行代碼的機(jī)器上進(jìn)行調(diào)試,符號(hào)文件中的源碼路徑會(huì)指向正確的位置,所以不需要設(shè)置源代碼路徑。如果所執(zhí)行代碼是在另一臺(tái)機(jī)器上生成的,你可以將所用的源碼拷貝(保持原有的目錄結(jié)構(gòu))的一個(gè)可以訪問(wèn)的文件夾(可以是網(wǎng)絡(luò)路徑)并將源代碼路徑設(shè)為該文件夾的路徑。注意如果是遠(yuǎn)程調(diào)試,你需要使用.lsrcpath來(lái)設(shè)置源碼路徑。
2)啟動(dòng)Debugger
Windbg可以用于如下三種調(diào)試:
(1)遠(yuǎn)程調(diào)試:你可以從機(jī)器A上調(diào)試在機(jī)器B上執(zhí)行的程序。具體步驟如下:
在機(jī)器B上啟動(dòng)一個(gè)調(diào)試窗口(Debug Session)。你可以直接在Windbg下運(yùn)行一個(gè)程序或者將Windbg附加(Attach)到一個(gè)進(jìn)程。
在機(jī)器B的Windbg命令窗口上啟動(dòng)一個(gè)遠(yuǎn)程調(diào)試接口(remote):
.server npipe:pipe=PIPE_NAME
PIPE_NAME是該接口的名字。
在機(jī)器A上運(yùn)行:
windbg –remote npipe:server=SERVER_NAME,pipe=PIPE_NAME
SERVER_NAME是機(jī)器B的名字。
Dump文件調(diào)試:如果在你的客戶(hù)的機(jī)器上出現(xiàn)問(wèn)題,你可能不能使用遠(yuǎn)程調(diào)試來(lái)解決問(wèn)題。你可以要求你的用戶(hù)將Windbg附加到出現(xiàn)問(wèn)題的進(jìn)程上,然后在命令窗口中輸入:
.dump /ma File Name
創(chuàng)建一個(gè)Dump文件。在得到Dump文件后,使用如下的命令來(lái)打開(kāi)它:
windbg –z DUMP_FILE_NAME
(2)本地進(jìn)程調(diào)試:你可以在Windbg下直接運(yùn)行一個(gè)程序:
Windbg “path to executable” arguments
也可以將Windbg附加到一個(gè)正在運(yùn)行的程序:
Windbg –p “process id”
Windbg –pn “process name”
注意有一種非侵入(Noninvasive)模式可以用來(lái)檢查一個(gè)進(jìn)程的狀態(tài)并不進(jìn)程的執(zhí)行。當(dāng)然在這種模式下無(wú)法控制被調(diào)試程序的執(zhí)行。這種模式也可以用于查看一個(gè)已經(jīng)在Debugger控制下運(yùn)行的進(jìn)程。具體命令如下:
Windbg –pv –p “process id”
Windbg –pv –pn “process name”
(3)調(diào)試多個(gè)進(jìn)程和線程
如果你想控制一個(gè)進(jìn)程以及它的子進(jìn)程的執(zhí)行,在Windbg的命令行上加上-o選項(xiàng)。Windbg中還有一個(gè)新的命令.childdbg 可以用來(lái)控制子進(jìn)程的調(diào)試。如果你同時(shí)調(diào)試幾個(gè)進(jìn)程,可以使用 | 命令來(lái)顯示并切換到不同的進(jìn)程。
在同一個(gè)進(jìn)程中可能有多個(gè)線程。~命令可以用來(lái)顯示和切換線程。
.hh keyword 如何得到幫助
靜態(tài)命令:
顯示調(diào)用堆棧:在連接到一個(gè)調(diào)試窗口后,首先要知道的就是程序當(dāng)前的執(zhí)行情況k* 命令顯示當(dāng)前線程的堆棧。~*kb會(huì)顯示所有線程的調(diào)用堆棧。如果堆棧太長(zhǎng),Windbg只會(huì)顯示堆棧的一部分。.kframes可以用來(lái)設(shè)置缺省顯示框架數(shù)。
顯示局部變量:接下來(lái)要做通常是用dv顯示局部變量的信息。CTRL+ALT+V可以切換到更詳細(xì)的顯示模式。關(guān)于dv要注意的是在優(yōu)化過(guò)的代碼中dv的輸出極有可能是不準(zhǔn)確的。這時(shí)后你能做的就是閱讀匯編代碼來(lái)發(fā)現(xiàn)你感興趣的值是否存儲(chǔ)在寄存器中或堆棧上。有時(shí)后當(dāng)前的框架(Frame)上可能找不到你想知道的數(shù)據(jù)。如果該數(shù)據(jù)是作為參數(shù)傳到當(dāng)前的方法中的,可以讀一讀上一個(gè)或幾個(gè)框架的匯編代碼,有可能該數(shù)據(jù)還在堆棧的某個(gè)地址上。靜態(tài)變量是儲(chǔ)存在固定地址中的,所以找出靜態(tài)變量的值較為容易。.Frame(或者在調(diào)用堆棧窗口中雙擊)可以用來(lái)切換當(dāng)前的框架。注意dv命令顯示的是當(dāng)前框架的內(nèi)容。你也可在watch窗口中觀察局部變量的值。
顯示類(lèi)和鏈表: dt可以顯示數(shù)據(jù)結(jié)構(gòu)。比如dt PEB 會(huì)顯示操作系統(tǒng)進(jìn)程結(jié)構(gòu)。在后面跟上一個(gè)進(jìn)程結(jié)構(gòu)的地址會(huì)顯示該結(jié)構(gòu)的詳細(xì)信息:dt PEB 7ffdf000。
Dl命令可以顯示一些特定的鏈表結(jié)構(gòu)。
顯示當(dāng)前線程的錯(cuò)誤值:!gle會(huì)顯示當(dāng)前線程的上一個(gè)錯(cuò)誤值和狀態(tài)值。!error命令可以解碼HRESULT。
搜索或修改內(nèi)存:使用s 命令來(lái)搜索字節(jié),字或雙字,QWORD或字符串。使用e命令來(lái)修改內(nèi)存。
計(jì)算表達(dá)式:?命令可以用來(lái)進(jìn)行計(jì)算。關(guān)于表達(dá)式的格式請(qǐng)參照幫助文檔。使用n命令來(lái)切換輸入數(shù)字的進(jìn)制。
顯示當(dāng)前線程,進(jìn)程和模塊信息:!teb顯示當(dāng)前線程的環(huán)境信息。最常見(jiàn)的用途是查看當(dāng)前線程堆棧的起始地址,然后在堆棧中搜索值。!peb顯示當(dāng)前進(jìn)程的環(huán)境信息,比如執(zhí)行文件的路徑等等。lm顯示進(jìn)程中加載的模塊信息。
顯示寄存器的值:r命令可以顯示和修改寄存器的值。如果要在表達(dá)式中使用寄存器的值,在寄存器名前加@符號(hào)(比如@eax)。
顯示最相近的符號(hào):ln Address。如果你有一個(gè)C++對(duì)象的指針,可以用來(lái)ln來(lái)查看該對(duì)象類(lèi)型。
查找符號(hào):x命令可以用來(lái)查找全局變量的地址或過(guò)程的地址。x命令支持匹配符號(hào)。x kernel32!*顯示Kernel32.dll中的所有可見(jiàn)變量,數(shù)據(jù)結(jié)構(gòu)和過(guò)程。
查看lock:!locks顯示各線程的鎖資源使用情況。對(duì)調(diào)試死鎖很有用。
查看handle:!handle顯示句柄信息。如果一段代碼導(dǎo)致句柄泄漏,你只需要在代碼執(zhí)行前后使用!handle命令并比較兩次輸出的區(qū)別。有一個(gè)命令!htrace對(duì)調(diào)試與句柄有關(guān)的Bug非常有用。在開(kāi)始調(diào)試前輸入:
!htrace –enable
然后在調(diào)試過(guò)程中使用!htrace handle_value 來(lái)顯示所有與該句柄有關(guān)的調(diào)用堆棧。
顯示匯編代碼:u。
程序執(zhí)行控制命令:
設(shè)置代碼斷點(diǎn):bp/bu/bm 可以用來(lái)設(shè)置代碼斷點(diǎn)。你可以指定斷點(diǎn)被跳過(guò)的次數(shù)。假設(shè)一段代碼KERNEL32!SetLastError在運(yùn)行很多次后會(huì)出錯(cuò),你可以設(shè)置如下斷點(diǎn):
bp KERNEL32!SetLastError 0x100.
在出錯(cuò)后使用bl 來(lái)顯示斷點(diǎn)信息(注意粗體顯示的值):
0 e 77e7a3b0 004f (0100) 0:*** KERNEL32!SetLastError
重新啟動(dòng)調(diào)試(.restart命令)并設(shè)置如下的斷點(diǎn):
bp Kernel32!SetLastError 0x100-0x4f
Debugger會(huì)停在出錯(cuò)前最后一次調(diào)用該過(guò)程的地方。
你可以指定斷點(diǎn)被激活時(shí)Debugger應(yīng)當(dāng)執(zhí)行的命令串。在該命令串中使用J命令可以用來(lái)設(shè)置條件斷點(diǎn):
bp `mysource.cpp:143` "j (poi(MyVar)”0n20) ''; 'g' "
上面的斷點(diǎn)只在MyVar的值大于32時(shí)被激活(g命令
條件斷點(diǎn)的用途極為廣泛。你可以指定一個(gè)斷點(diǎn)只在特殊的情況下被激活,比如傳入的參數(shù)滿(mǎn)足一定的條件,調(diào)用者是某個(gè)特殊的過(guò)程,某個(gè)全局變量被設(shè)為特殊的值等等。
設(shè)置內(nèi)存斷點(diǎn):ba可以用來(lái)設(shè)置內(nèi)存斷點(diǎn)。調(diào)試過(guò)程中一個(gè)常見(jiàn)的問(wèn)題是跟蹤某些數(shù)據(jù)的變化。如下的斷點(diǎn):
ba w4 0x40000000 "kb; g"
可以打印出所有修改0x40000000的調(diào)用堆棧。
控制程序執(zhí)行:p, pa,t, ta等命令可以用來(lái)控制程序的執(zhí)行。
控制異常和事件處理:Debugger的缺省設(shè)置是跳過(guò)首次異常(first chance expcetion),在二次異常(second chance exception)時(shí)中斷程序的執(zhí)行。sx命令顯示Debugger的設(shè)置。sxe和sxd可以改變Debugger的設(shè)置。
sxe clr
可以控制Debugger在托管異常發(fā)生時(shí)中斷程序的執(zhí)行。常用的Debugger事件有:
av 訪問(wèn)異常
eh C++異常
clr 托管異常
ld 模塊加載
-c 選項(xiàng)可以用來(lái)指定在事件發(fā)生時(shí)執(zhí)行的調(diào)試命令。
堆棧顯示指令kb , kp, kP , kv
反匯編指令 u,uf
跟蹤指令 T,TA,TB,TC
執(zhí)行相關(guān)指令 P,PA,PC
跟蹤查看指令 WT
----------------------------------------------------------------------------
堆棧顯示指令
k [b|p|P|v]
在內(nèi)核調(diào)試的時(shí)候,k命令用來(lái)顯示內(nèi)核棧的內(nèi)容
先說(shuō)說(shuō)內(nèi)核棧用來(lái)干嘛的 看了些資料個(gè)人理解是這樣的
比如我們的代碼運(yùn)行時(shí),肯定會(huì)有函數(shù)函數(shù)然后還會(huì)調(diào)用函數(shù) 但是系統(tǒng)如何記錄是哪個(gè)父函數(shù)調(diào)用了這個(gè)子函數(shù),在子函數(shù)調(diào)用之前整個(gè)狀態(tài)又是怎樣的,其實(shí)系統(tǒng)是利用了堆棧記錄的 棧這個(gè)東西好阿 先進(jìn)后出 最近調(diào)用的函數(shù)記錄在最頂層 函數(shù)執(zhí)行完后就從棧內(nèi)彈出之前記錄的參數(shù),如果調(diào)用函數(shù) 一樣的把函數(shù)壓進(jìn)棧內(nèi)就好了 這樣一來(lái) 一旦子函數(shù)執(zhí)行完,從棧內(nèi)彈出的第一個(gè)函數(shù)肯定是該子函數(shù)的老爹 我們可以看上層堆棧的狀態(tài)等等 功能大家慢慢去體會(huì)吧我也沒(méi)用過(guò) 呵呵 不好說(shuō)什么 下面說(shuō)些細(xì)節(jié)的東西
b
顯示傳給函數(shù)的前三個(gè)參數(shù)
p
顯示傳給函數(shù)的全部參數(shù)
P( 大寫(xiě))
跟上面那個(gè)一樣 只不過(guò)是顯示形式不同而已
V
外加顯示一些額外的信息
----------------------------------------------------------------------------
u [f]
反匯編指令,嘿嘿 超級(jí)有用的指令喲雖然說(shuō)內(nèi)核很多東西很復(fù)雜 認(rèn)識(shí)偶爾小小反下也是可以的
u
反匯編當(dāng)前寄存器指向的代碼
uf 函數(shù)名(比如nt!ZwCreateFile)
反匯編指定的函數(shù)
----------------------------------------------------------------------------
t [r]
單步跟蹤
r 打開(kāi)指顯示寄存器的詳細(xì)信息,狀態(tài)的開(kāi)關(guān)(下面指令一樣有效,在用1次就會(huì)關(guān)閉哦~)
ta 地址
讓程序執(zhí)行到指定地址
tb
讓程序運(yùn)行到分支語(yǔ)句時(shí)停止
tc
讓程序運(yùn)行到下一個(gè)函數(shù)調(diào)用停止
----------------------------------------------------------------------------
p [r]
單步執(zhí)行一跳指令
r 打開(kāi)指顯示寄存器的詳細(xì)信息,狀態(tài)的開(kāi)關(guān)(下面指令一樣有效,在用1次就會(huì)關(guān)閉哦~)
pa
讓程序執(zhí)行到指定地址
pc
讓程序執(zhí)行到函數(shù)調(diào)用就停止
----------------------------------------------------------------------------
wt
在想查看指定函數(shù)的信息而又不想單步通過(guò)該函數(shù)時(shí)很有用。可以到函數(shù)的起始地址并執(zhí)行 wt 命令。(摘自翻譯文檔)
這個(gè)感覺(jué)用處不是很大.不細(xì)細(xì)研究了
----------------------------------------------------------------------------
Ps: 很多人不清楚到底p指令和t指令有什么區(qū)別 其實(shí)很簡(jiǎn)單 p指令執(zhí)行到函數(shù)時(shí)把這個(gè)當(dāng)做一個(gè)指令來(lái)執(zhí)行也就是說(shuō)不會(huì)進(jìn)入函數(shù)執(zhí)行,但是t指令會(huì)進(jìn)入到函數(shù)里面執(zhí)行 就這么簡(jiǎn)單~~呵呵
遠(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)試客戶(hù)端’指的是控制當(dāng)前會(huì)話的調(diào)試器。
· 使用調(diào)試器:你需要CDB, NTSD或者WinDbg已經(jīng)安裝在遠(yuǎn)程機(jī)器上。WinDbg客戶(hù)端可以連接到CDB, NTSD或者WinDbg中的任何一個(gè)作為服務(wù)器,反之亦然。在客戶(hù)端和服務(wù)器直接可以選擇TCP或者命名管道作為通訊協(xié)議。
o 在服務(wù)器端的啟動(dòng)過(guò)程:
§ WinDbg –server npipe:pipe=pipename(注:可以允許多個(gè)客戶(hù)端連接)
§ 從WinDbg內(nèi)部: .server npipe:pipe=pipename(注,連接單個(gè)客戶(hù)端)
你可以用多種協(xié)議開(kāi)啟不同的服務(wù)會(huì)話。并且可用密碼來(lái)保護(hù)一個(gè)會(huì)話。
o 從客戶(hù)端連接:
§ 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來(lái)遠(yuǎn)程調(diào)試。注意:使用@q(不是q)來(lái)退出客戶(hù)端,不用關(guān)掉服務(wù)端。
o 要啟動(dòng)一個(gè)服務(wù)端:
§ Remote.exe /s “cdp –p <pid>” test1
o 從客戶(hù)端連接:
§ Remote.exe /c <machinename> test1
上面的test1是我們所選擇的命名管道的名字。
服務(wù)端會(huì)顯示那個(gè)客戶(hù)端從那個(gè)服務(wù)器連接以及執(zhí)行過(guò)的命令。你可以使用‘qq’命令來(lái)退出服務(wù)端;或者使用File->Exit來(lái)退出客戶(hù)端。另外,如果要進(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è)置)
通過(guò)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)部開(kāi)發(fā)代號(hào)) .net CLR托管調(diào)試。在文檔中針對(duì)托管調(diào)試有很好的討論。需要注意的是,對(duì)于托管程序來(lái)說(shuō),沒(méi)有剛才所說(shuō)的PDB(譯注:托管代碼實(shí)際上也是有PDB的,但是這個(gè)PDB實(shí)際上記錄了C#代碼和IL代碼的對(duì)應(yīng)關(guān)系以及相關(guān)的一些信息)的概念,因?yàn)樗械某绦蚨际蔷幾g成為ILASM。調(diào)試器通過(guò)CLR來(lái)查詢(xún)所需的附加信息。
有幾點(diǎn)需要注意:
你只能在托段函數(shù)的代碼被執(zhí)行過(guò)至少一次之后才能設(shè)置斷點(diǎn)。只有這樣它才能被編譯成匯編代碼。記住以下的幾點(diǎn):
· 關(guān)于函數(shù)的地址的復(fù)雜化以及對(duì)應(yīng)的斷點(diǎn)設(shè)置:
o CLR有可能丟棄已經(jīng)編譯好的代碼,所以函數(shù)的入口地址有可能改變。
o 同樣的代碼有可能被多次編譯,如果多個(gè)應(yīng)用程序域沒(méi)有共享這段代碼的話。如果你設(shè)置了一個(gè)斷點(diǎn),它就會(huì)被設(shè)置在當(dāng)前線程(譯注:CLR的邏輯線程)所在的應(yīng)用程序域內(nèi)。
o 泛型的特殊實(shí)例可能導(dǎo)致同一個(gè)函數(shù)有不同的地址。.
· 數(shù)據(jù)存儲(chǔ)布局的復(fù)雜化以及對(duì)應(yīng)的數(shù)據(jù)檢查:
CLR可能會(huì)在運(yùn)行的時(shí)候任意改變數(shù)據(jù)的存儲(chǔ)布局,所以一個(gè)結(jié)構(gòu)體成員的偏移量可能會(huì)被改變掉. (譯注:實(shí)際上是在一個(gè)類(lèi)型被加載的時(shí)候決定的數(shù)據(jù)布局,之后是不會(huì)改變的。)
一個(gè)類(lèi)型的信息是在第一次使用的時(shí)候被加載,所以你可能不能夠查看一個(gè)數(shù)據(jù)成員如果它還沒(méi)有被使用過(guò).
· 調(diào)試器命令的復(fù)雜化
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ù)程序。即可以通過(guò)附加進(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)用程序沒(méi)有處理這個(gè)異常,這時(shí)候調(diào)試器就會(huì)有機(jī)會(huì)來(lái)處理異常(‘second-chance exception’)。如果調(diào)試器沒(méi)有處理二次機(jī)會(huì)的異常,應(yīng)用程序就會(huì)退出。
.lastevent或者,!analyze –v命令會(huì)給你顯示異常的記錄以及異常拋出所在函數(shù)的堆棧跟蹤信息。
你也可以使用 .exr, .cxr以及 .ecxr命令來(lái)顯示異常和上下文記錄。同時(shí)需要注意的是,你也可以改變first-chance的處理選項(xiàng)。對(duì)應(yīng)的命令就是: sxe, sxd, sxn和sxi。
虛擬機(jī)調(diào)試驅(qū)動(dòng)
1)
將 WinDbg 發(fā)送一個(gè)快捷方式,并修改在快捷方式上右鍵=>"屬性"
將"目標(biāo)"中的 WinDbg 文件名后添加 "-k com:port=\\.\pipe\com_1,baud=115200,pipe" , 如下:
"C:\Program Files\Debugging Tools for Windows\windbg.exe" -k com:port=\\.\pipe\com_1,baud=115200,pipe
2)
打開(kāi)虛擬機(jī)中的 c:\boot.ini 文件(之前去掉"只讀"屬性),復(fù)制一行
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise" /fastdetect
即添加了一個(gè)啟動(dòng)選項(xiàng),并修改為:
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise[Debug]" /fastdetect /debug /debugport=com1 /baudrate=115200
即添加了調(diào)試選項(xiàng),調(diào)試端口以及串口的速率.
保存.
3)
關(guān)閉虛擬機(jī)里的目標(biāo)windows系統(tǒng)(必須,否則在"Settings..."里的"Add..."將為灰色,不可選狀態(tài)),
選擇目標(biāo)windows系統(tǒng)的"Settings..."選項(xiàng),在"Hardware"選項(xiàng)中,點(diǎn)擊下面的"Add..."按鈕.
選擇"Serial Port"點(diǎn)擊"Next",再選擇"Output to named pipe","Next",
這一向?qū)е?前兩項(xiàng)不修改,最后一項(xiàng)修改為"The other end is an application.",
如果這里存在"高級(jí)"選項(xiàng),則在其中選擇"Yield CPU on poll"[注:有些虛擬機(jī)在這里并沒(méi)有"高級(jí)"選項(xiàng),則在"Finish"后,選擇"Serial Port",再勾選右下角的"Yield CPU on poll"],
"Finish".
"OK",完成"Virtual Maching Setting".
4)
打開(kāi)虛擬機(jī)中的目標(biāo)Windows系統(tǒng),選擇"Windows Server 2003, Enterprise[Debug]"后,立即打開(kāi)之前創(chuàng)建的WinDbg快捷方式(若先打開(kāi)WinDbg,則會(huì)報(bào)找不到'com:port=....'等信息).
5)
連接上虛擬機(jī)中的目標(biāo)windows系統(tǒng)后,立即發(fā)送一個(gè)WinDbg中的"Debug"=>"Break"來(lái)中斷它.
6)
設(shè)置"Symbol"路徑,"Source"路徑和"ImagePath"路徑(若有多個(gè),則用";"號(hào)隔開(kāi)).
如:
"File"=>"Symbol" 里設(shè)置:"G:\虛擬機(jī)共享文件\Win2003Symbols;G:\虛擬機(jī)共享文件\GiveIO",勾選"Reload","OK". (第一個(gè)是Windows的符號(hào)[這里要注意,符號(hào)應(yīng)該是虛擬機(jī)內(nèi)Windows系統(tǒng)對(duì)應(yīng)的],第二個(gè)是要調(diào)試的驅(qū)動(dòng)的符號(hào))
"File"=>"Source" 里設(shè)置:"G:\虛擬機(jī)共享文件\GiveIO", "OK". (要調(diào)試的驅(qū)動(dòng)的源碼我放在了此目錄中)
"File"=>"Image File Path" 里設(shè)置: "G:\虛擬機(jī)共享文件\GiveIO", "Reload", "OK". (我把要調(diào)試的驅(qū)動(dòng)程序放在了此目錄中)
"File"=>"Open Source File", 打開(kāi)驅(qū)動(dòng)源文件.
7)
在要調(diào)試的驅(qū)動(dòng)程序上下斷點(diǎn)(這里我調(diào)試的驅(qū)動(dòng)模塊名為"GiveIO",后面是在"DriverEntry"入口點(diǎn)下斷點(diǎn)[bu設(shè)置的是延遲斷點(diǎn)]):
bu GiveIO!DriverEntry
按"F5"(或在命令行中用"g"命令),即可讓虛擬機(jī)中的目標(biāo)Windows順利啟動(dòng).
8)
在虛擬機(jī)中運(yùn)行要調(diào)試的驅(qū)動(dòng)程序,即可運(yùn)行到斷點(diǎn)處.
9)
一些說(shuō)明:
在已運(yùn)行的Windows目標(biāo)系統(tǒng)中調(diào)試指定驅(qū)動(dòng):
首先應(yīng)使WinDbg產(chǎn)生一個(gè)斷點(diǎn),使用WinDbg中的"Debug"=>"Break",此時(shí)目標(biāo)Windows系統(tǒng)就會(huì)中斷.
此時(shí)使用:
bu GiveIO!DriverEntry
命令,在指定驅(qū)動(dòng)的模塊名的入口點(diǎn)下斷點(diǎn)(GiveIO為此處的驅(qū)動(dòng)模塊名).
運(yùn)行驅(qū)動(dòng)后,即會(huì)在驅(qū)動(dòng)的DriverEntry處斷下來(lái),此時(shí)就可以進(jìn)行跟蹤調(diào)試了.
bl命令可以查看已下的斷點(diǎn)
bc清除斷點(diǎn),如果后面跟"*",則清除所有斷點(diǎn)