標籤:
原文連結地址:http://blog.csdn.net/Donjuan/article/details/4618717 函數斷點
在前面的文章Visual Studio調試之避免單步跟蹤偵錯模式裡面我講了如何設定函數斷點,說實話,我個人喜歡設定函數斷點,而不是在程式碼裡面設定斷點。一般來說,函數斷點在下面幾種情形下有用:
1. 例如調試一個網站程式,你通過分析網站的日誌發現最有可能發生錯誤的函數,開啟調試器並將調試器附加到程式上去,設定函數斷點,重新執行網站……這樣做的好處是,不用到處開啟源檔案去找出錯的原始碼行,調試器會自動開啟原始碼,並且在函數的入口處中斷(豈不是很方便?)。
2. 例如你在閱讀原始碼的時候,通常在讀到虛函數調用的時候,因為通常這種調用都是通過基類指標調用的,而你又一時半會不知道到底有哪個繼承類的Overloading函數會被調用到,函數斷點可以告訴你。
3. 或者一種特殊的情形,你想讀一個程式的原始碼,但就是找不到入口Main函數,例如.NET程式,那麼直接在Visual Studio裡面按F11就能幫你找到入口函數—這是函數斷點的一個特殊情形。
4. 比如你在調試Web Service函數,設定函數斷點也是一個快捷的調試方法,這個技巧跟技巧1類似。
當然,可能有些讀者沒有辦法成功設定函數斷點,如果設定函數斷點失敗,請閱讀我的文章“不能設定斷點的檢查步驟”。如果裡面有一些名詞術語(術語請參看文章:調試術語)不知道或者不知道如何設定的話,呃,我會另寫一篇文章講解。
斷點編程
有的時候你可能會碰到這種情況,觸發一個斷點以後,你發現需要修改一些值,才能使程式繼續正確執行下去。例如我以前在中文版本的作業系統上,使用sscli裡面(調試版)的csc.exe編譯器編譯一些包含語法錯誤或者文法警告的C#源檔案的時候,csc.exe總是會莫名其妙地報告內部嚴重錯誤,然後就崩潰了。我將調試器附加上去以後,發現是一個ASSERT錯誤,ASSERT(lcid == 0x409),表示sscli裡面的csc.exe總是預設自己在英文作業系統(或者說英文環境)裡面運行。而且這一條語句會被執行很多次,手工修改lcid的值的確有點麻煩。然後我找原始碼找來找去都沒有找到csc.exe在哪個地方擷取到這個lcid值(當然我最後找到了,下一個技巧將告訴你我是怎麼找到的),然而我又不想重啟系統(呃……也許我就是那種寧願花1個鐘頭去找節省花費5分鐘重啟系統的方法的那種人……)。
這個時候如果調試器可以自動幫你重設lcid的值該有多好?幸運的是,Visual Studio提供了方法讓你完成這樣的工作。下面是一個簡化的代碼,因為我一時半會找不到sscli了:
int lcid = System.Globalization.CultureInfo.CurrentUICulture.LCID;
Console.WriteLine("lcid = {0}", lcid);
上面的代碼在正常情況下,應該返回當前作業系統語言的lcid值,例如英文就是1033,中文的,呃……我忘記了。假設我們現在希望做的是,每當lcid的值為1033的時候,就自動校正為0。我們需要:
1. 在Console.WriteLine這一行上設定一個條件斷點,條件斷點的設定請參看Visual Studio調試之斷點進階篇:
2. 點擊Visual Studio功能表列裡面的“工具(Tools)”—“宏(Macro)”—“宏資源管理員(Macro Explorer)”。然後建立一個新的宏:
Imports SystemImports EnvDTEImports EnvDTE80Imports EnvDTE90Imports System.DiagnosticsImports Microsoft.VisualBasicImports Microsoft.VisualBasic.ControlCharsPublic Module Module1Sub ChangeExpression()DTE.Debugger.ExecuteStatement("lcid = 0;")End SubEnd Module
上面DTE.Debugger.ExecuteStatement的作用,你可以理解成在立即視窗中執行lcid = 0;這條語句。
3. 右鍵點擊剛才設定好的斷點,在菜單裡面選擇“When Hit …”,這一次在“When Breakpoint is Hit”視窗中勾選“Run a macro:(執行一個宏)”,然後在下拉框裡面選擇剛才你建立的宏的名稱。如果你是第一次建立宏,名稱應該是:Macros.MyMacros.Module1.ChangeExpression。
4. 勾選“繼續執行(Continue execution)”,因為我們並不想讓程式中斷下來。
5. 點擊確定以後,執行程式看一看結果,lcid是不是已經被自動改成0了?
資料斷點
注意,這個技巧僅對C++程式調試有效(或者說native程式),而且你只能在中斷模式下才能設定資料斷點,另外你還只能在本機設定資料斷點。
上一節的例子裡,我們提到了,有的時候一個全域變數被修改了以後,你可能都找不到它是什麼時候被修改的,於是夜已深,人已寐,你還在辛苦地調試到底是哪個鬼地方把這個變數的值修改了。F11, F10,……,SHIFT + F11,……,F5,靠,調過了,重來,F11,F10,……
這種情況下,資料斷點就很有用了,Visual Studio允許你在變數被修改的時候,中斷程式的執行,是不是很酷?
預設情況下,你是找不到資料斷點這個菜單的,需要執行下面的步驟把它拉出來:
1. 開啟你要調試的項目。
2. 點擊Visual Studio功能表列裡面的“工具(Tools)”—“自訂(Customize…)”。然後在“自訂(Customize…)”視窗中選擇“命令(Commands)”頁簽裡面的“種類(Categories)”列表框裡的“調試(Debug)”,找到“新資料斷點(New Data Breakpoint)”,將它拖到功能表列裡面相應的位置。
然後開啟或者建立一個C++項目,我們以下面的原始碼為例子:
#include "stdafx.h"int g_Variable = 0;int _tmain(int argc, _TCHAR* argv[]){ printf("Before modifying data breakpoints/n"); g_Variable = 1; printf("After modifying data breakpoints/n"); return 0;}
我們現在要Visual Studio在更改g_Variable的時候中斷程式的執行。
1. 單擊F11,這樣程式就會在_tmain函數裡面中斷了,我們也就有機會設定資料斷點了。
2. 點擊菜單裡面的“新資料斷點(New Data Breakpoint)”。注意,資料斷點是通過監視記憶體位址某一段地區更改來實現的,因此你必須提供一個記憶體位址(或者說就是指標吧),這裡g_Variable是一個整形變數,因此你需要使用“&g_Variable”的形式來建立一個資料斷點,因為整形的 大小是4個位元組,因此資料斷點監視的地區是4個位元組,如所示:
3. 繼續程式的執行,這時會彈出一個對話方塊,告訴你有一個記憶體位址的內容發生了變化(說明我們的資料斷點生效了),這時程式碼指向的是資料被修改的下一行代碼,為什麼會是下一行代碼,下一篇文章會講到:
呃,為什麼資料斷點只能在C++/C程式中才能設定?是因為Managed 程式碼有記憶體回收。而資料斷點的執行原理應該是Windows記憶體管理裡面的Guard Pages概念和VirtualProtectEx函數的實現。這個概念可以自己去查MSDN的記憶體管理方面的文檔。
Visual Studio調試之斷點技巧篇