|
文章導(dǎo)航
Visual Studio調(diào)試之?dāng)帱c(diǎn)基礎(chǔ)篇
Visual Studio調(diào)試之?dāng)帱c(diǎn)進(jìn)階篇
Visual Studio調(diào)試之?dāng)帱c(diǎn)技巧篇
在上一篇文章Visual Studio調(diào)試之?dāng)帱c(diǎn)基礎(chǔ)篇里面介紹了什么是斷點(diǎn),INT 是Intel系列CPU的一個(gè)指令,可以讓程序產(chǎn)生一個(gè)中斷或者異常。程序中如果有中斷或者異常發(fā)生了以后,CPU會(huì)中斷程序的執(zhí)行,去一個(gè)叫做IDT的部件查找處理這個(gè)中斷(或者異常)的例程(Handler)。IDT是操作系統(tǒng)在啟動(dòng)的時(shí)候初始化的,至于IDT的細(xì)節(jié)問(wèn)題,例如什么是IDT,怎樣編寫(xiě)一個(gè)IDT的例程,怎樣 初始化IDT,可以去網(wǎng)上搜索一些資料。
總之,這里我們只要知道,CPU在執(zhí)行程序指令過(guò)程中,碰到INT 3中斷程序的執(zhí)行,CPU然后去IDT表里面找到處理斷點(diǎn)的例程入口。這個(gè)例程要做的事情就是:
1. 先看看機(jī)器里面是不是安裝了一個(gè)調(diào)試器—記住,這一步很重要,之所以重要以后的文章里面會(huì)介紹。
2. 如果機(jī)器里面沒(méi)有安裝調(diào)試器,那么操作系統(tǒng)就會(huì)終止程序的執(zhí)行。
3. 否則操作系統(tǒng)啟動(dòng)調(diào)試器,并將調(diào)試器附到進(jìn)程上。
4. 這樣,我們才能在調(diào)試器里面檢查程序內(nèi)部變量的值。
前面文章里面的INT 3 (或者DebugBreak(),或者Debugger.Break())指令是我們自己在代碼里面硬編碼進(jìn)去的,因此我們?cè)?/span>Visual Studio里,在相應(yīng)的代碼行里面點(diǎn)一下,出現(xiàn)一個(gè)小紅球,也就是說(shuō)Visual Studio在程序指令集某個(gè)地方動(dòng)態(tài)地添加了一個(gè)INT 3指令。現(xiàn)在的問(wèn)題來(lái)了,Visual Studio是如何在程序中正確找到插入INT 3指令的位置的?
或者更具體一些,我們?cè)谠创a(文本文件)里面設(shè)置斷點(diǎn)的,Visual Studio需要把代碼行翻譯成在程序指令集中的位置。Visual Studio之所以需要做翻譯,是因?yàn)橥ǔR恍?/span>C++或者 C#代碼都會(huì)對(duì)應(yīng)好幾行匯編指令。
因此,Visual Studio需要一個(gè)額外的文件來(lái)執(zhí)行這個(gè)翻譯過(guò)程,這個(gè)額外的文件叫做調(diào)試符號(hào)文件(Symbols),是由編譯器生成的。Visual Studio系列的編譯器,不論是C#、VB.NET還是C++編譯器都會(huì)生成這個(gè)調(diào)試符號(hào)文件,.pdb 文件。所以如果你花一點(diǎn)時(shí)間看看Debug文件夾的話,你就會(huì)發(fā)現(xiàn)這個(gè)文件。
因此我們來(lái)看看Visual Studio支持的各種斷點(diǎn),并解釋各種斷點(diǎn)的實(shí)現(xiàn)方式
條件斷點(diǎn)
首先我們先看看如何設(shè)置條件斷點(diǎn),條件斷點(diǎn)有兩種,一種是根據(jù)觸發(fā)的次數(shù)來(lái)設(shè)置,另外一種是根據(jù)一條預(yù)置的條件來(lái)設(shè)置。
根據(jù)觸發(fā)次數(shù)設(shè)置
比如說(shuō),你有一個(gè)循環(huán),循環(huán)1000次,你知道有一個(gè)BUG總是在500次之后才會(huì)出現(xiàn),因此肯定希望在循環(huán)內(nèi)設(shè)置一個(gè)斷點(diǎn),但是前面500次都不會(huì)觸發(fā)這個(gè)斷點(diǎn),否則連續(xù)按500次的F5的確不是一件輕松的差事。
根據(jù)預(yù)置條件來(lái)設(shè)置
如果你已經(jīng)知道一些條件可能會(huì)引發(fā)Bug,那么根據(jù)條件來(lái)設(shè)置則最合適不過(guò)了。如下圖所示:
在“斷點(diǎn)條件(Breakpoint Condition)”對(duì)話框里面,只需要輸入一條正常的C#、C++或者VB.NET的語(yǔ)句就可以了(當(dāng)然,語(yǔ)法是根據(jù)你項(xiàng)目里面的源代碼語(yǔ)法一致),這條語(yǔ)句的要求是必須返回bool值—否則就不是一個(gè)條件了。
第三個(gè)還有斷點(diǎn)過(guò)濾器,當(dāng)你在斷點(diǎn)上,右鍵點(diǎn)擊彈出的菜單里面,會(huì)有一個(gè)“過(guò)濾(Filter)”菜單,它允許你限制將斷點(diǎn)僅設(shè)置在特定的線程上。這里我就不細(xì)講了,有興趣的話,可以自己寫(xiě)一個(gè)多線程或者多進(jìn)程程序試試這個(gè)功能。
知道斷點(diǎn)的原理以后,理解條件斷點(diǎn)應(yīng)該就不會(huì)是問(wèn)題了。
監(jiān)視斷點(diǎn)(Watching Point)
有的時(shí)候,你可能需要查看程序內(nèi)部一些變量的值,但是你又不希望中斷程序的執(zhí)行。例如你在調(diào)試一個(gè)網(wǎng)絡(luò)協(xié)議棧,一個(gè)程序可能在接收數(shù)據(jù)包,你想看看數(shù)據(jù)包的格式,但如果中斷程序的執(zhí)行,會(huì)導(dǎo)致后續(xù)的數(shù)據(jù)包丟失。
因此,我們一般的做法就是在源代碼里面加一些日志記錄代碼,這樣可以將一些變量的值記錄下來(lái),以便后續(xù)分析。如果日志在產(chǎn)品發(fā)布以后還需要的話,在源代碼里面加入這些日志代碼固然是一個(gè)好主意,但是如果你只是想臨時(shí)看看一些變量的值呢?
這個(gè)時(shí)候,監(jiān)視斷點(diǎn)就很有用了,Visual Studio的監(jiān)視斷點(diǎn)就可以讓你做到在不修改程序源代碼的前提下,在調(diào)試器窗口中打印一些變量的值。
下圖演示了監(jiān)視斷點(diǎn)的用法:
設(shè)置監(jiān)視斷點(diǎn)的步驟,或者說(shuō)是注意事項(xiàng)吧:
1. 設(shè)置一個(gè)普通的斷點(diǎn)
2. 右鍵單擊剛剛設(shè)置的斷點(diǎn),在彈出菜單里面選擇“When Hit…”
3. 鉤選 第一個(gè)“打印一條消息(Print a message)”復(fù)選框,輸入一串文本,默認(rèn)情況下,你輸入的文本會(huì)被直接打印到調(diào)試的輸出窗口里面來(lái)。除了:
a. 以$符號(hào)開(kāi)頭的幾個(gè)關(guān)鍵字。比如$FUNCTION就會(huì)被替換成斷點(diǎn)所在的函數(shù)名。其他有一些關(guān)鍵字在“When Breakpoints Is Hit”窗口當(dāng)中有詳細(xì)的說(shuō)明。
b. 使用 大括號(hào) {}包含起來(lái)的變量名,這樣的字符串會(huì)被替換成變量的值。
這下面就是監(jiān)視斷點(diǎn)的效果,注意,你只能在Visual Studio的“輸出(Output)”窗口中查看結(jié)果。
監(jiān)視斷點(diǎn)相對(duì)于日志記錄的好處是,你不需要改動(dòng)源代碼,并且重新編譯代碼。實(shí)際上Visual Studio實(shí)現(xiàn)監(jiān)視斷點(diǎn)的原理也很簡(jiǎn)單,就是插入一個(gè)普通的斷點(diǎn),斷點(diǎn)觸發(fā)之后處理并且打印在“When Breakpoints Is Hit”窗口輸出的表達(dá)式,最后自動(dòng)恢復(fù)程序的執(zhí)行。
NET技術(shù):Visual Studio調(diào)試之?dāng)帱c(diǎn)進(jìn)階篇,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。